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
аналогичное решение brianegge это на awk, вот это Перл эквивалент:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
включает режим autosplit, который заполняет@F
массив с данными столбца.
Используйте-F,
Если ваши данные разделены запятыми, а не пробелами.поле 3 печатается, так как Perl начинает отсчет от 0, а не 1
получение правильной строки (пример для строки № 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 }'
.