bash: разделить вывод команды по столбцам


Я хочу сделать это:

  1. выполнить команду
  2. снять выход
  3. выберите строку
  4. выберите столбец этой строки

просто в качестве примера, скажем, я хочу получить имя команды от $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 71

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 }'.

Баша set будет анализировать все выходные данные в параметры позиции.

например,, echo покажет "Mem:"