bash: разделить вывод команды по столбцам
Я хочу сделать это:
- выполнить команду
- снять выход
- выберите строку
- выберите столбец этой строки
в качестве примера предположим, что я хочу получить имя команды из $PID
(обратите внимание, что это всего лишь пример, я не предполагаю, что это самый простой способ получить имя команды из идентификатора процесса - моя реальная проблема с другой командой, формат вывода которой я не могу контроль.)
если я запускаю ps
Я:
PID TTY TIME CMD
11383 pts/1 00:00:00 bash
11771 pts/1 00:00:00 ps
теперь я делаю ps | egrep 11383
и вам
11383 pts/1 00:00:00 bash
следующий шаг: ps | egrep 11383 | cut -d" " -f 4
. Выход есть:
<absolutely nothing/>
проблема в том, что cut
отрезает выход одиночными пробелами, и как ps
добавляет некоторые пробелы между 2-м и 3-м столбцами, чтобы сохранить некоторое сходство таблицы,cut
выбирает пустую строку. Конечно, я мог бы использовать cut
чтобы выбрать 7-е, а не 4-е поле, но как я могу знать, особенно, когда выход является переменным и неизвестным заранее.
10 ответов
один простой способ-добавить проход tr
чтобы выдавить любые повторяющиеся разделители полей:
$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
Я думаю, что самый простой способ-использовать awk. Пример:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print ; }'
bash
обратите внимание:tr -s ' '
опция не удалит ни одного ведущего пробела. Если ваш столбец выровнен по правому краю (как в ps
pid)...
$ ps h -o pid,user -C ssh,sshd | tr -s " "
1543 root
19645 root
19731 root
тогда вырезание приведет к пустой строке для некоторых из этих полей, если это первый столбец:
$ <previous command> | cut -d ' ' -f1
19645
19731
если вы не предшествуете ему с пробелом, очевидно
$ <command> | sed -e "s/.*/ &/" | tr -s " "
теперь, для этого конкретного случая чисел pid (не имен), есть функция, называемая pgrep
:
$ pgrep ssh
функции оболочки
однако, в общем, на самом деле все еще можно использовать функции оболочки в краткой форме, потому что есть аккуратная вещь о :
$ <command> | while read a b; do echo $a; done
первый параметр для чтения, a
, выбирает первый столбец, и если есть больше,все остальное будет поставлен в b
. В результате вам никогда не нужно больше переменных, чем количество ваших колонка +1.
и
while read a b c d; do echo $c; done
затем выведет 3-й столбец. Как указано в моем комментарии...
конвейерное чтение будет выполняться в среде, которая не передает переменные вызывающему скрипту.
out=$(ps whatever | { read a b c d; echo $c; })
arr=($(ps whatever | { read a b c d; echo $c $b; }))
echo ${arr[1]} # will output 'b'`
Решение Массиве
таким образом, мы получаем ответ от @frayser, который должен использовать переменную оболочки IFS, которая по умолчанию имеет пробел, чтобы разделить строку на массив. Это только работает в Баш. Дэш и Эш не поддерживают его. Мне было очень сложно разбить строку на компоненты в Busybox. Достаточно легко получить один компонент (например, с помощью awk), а затем повторить это для каждого нужного вам параметра. Но затем вы снова и снова вызываете awk в одной строке или повторно используете блок чтения с echo в одной строке. Что не эффективно и не красиво. Таким образом, вы в конечном итоге разделяете с помощью ${name%% *}
и так далее. Заставляет вас тосковать по Питону навыки, потому что на самом деле оболочки сценариев не очень весело больше, если половина или более функций, к которым вы привыкли, ушли. Но вы можете предположить, что даже python не будет установлен в такой системе, и это не так ;-).
попробовать
ps |&
while read -p first second third fourth etc ; do
if [[ $first == '11383' ]]
then
echo got: $fourth
fi
done
подобно решению awk brianegge, вот эквивалент Perl:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
включает режим автозапуска, который заполняет @F
массив с данными столбца.
Использовать -F,
Если ваши данные разделены запятыми, а не пробелами.
поле 3 печатается, так как Perl начинает отсчет от 0, а не от 1
получение правильной строки (пример для строки no. 6) делается с головой и хвостом и правильным словом (слово нет. 4) может быть захвачен с awk:
command|head -n 6|tail -n 1|awk '{print }'
использование переменных массива
set $(ps | egrep "^11383 "); echo
или
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
вместо того, чтобы делать все эти greps и прочее, я бы посоветовал вам использовать возможности ps для изменения формата вывода.
ps -o cmd= -p 12345
вы получаете строку cmmand процесса с указанным pid и ничего больше.
Это POSIX-соответствует и, таким образом, может считаться портативным.
команда
ps | egrep 11383 | cut -d" " -f 4
мимо tr -s
сжать пространства, как unwind объясняет в ответ.
однако, вы, возможно, хотите использовать awk
, так как он обрабатывает все эти действия в одной команде:
ps | awk '/11383/ {print }'
это печатает 4-й столбец в тех строках, содержащих 11383
. Если вы хотите, чтобы это соответствовало 11383
если он появляется в начале строки, тогда вы можете сказать ps | awk '/^11383/ {print }'
.
Баша set
будет анализировать все выходные данные в параметры позиции.
например,, echo
покажет "Mem:"