Я хочу сделать это:
- запустить команду
- захватить вывод
- выберите строку
- выберите столбец этой строки
В качестве примера, допустим, я хочу получить имя команды из $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-го поля, но как я могу знать, особенно когда выходные данные являются переменными и неизвестными заранее.
Ответы:
Один простой способ - добавить проход,
tr
чтобы убрать любые повторяющиеся разделители полей:$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
источник
tr
он легче, чемawk
Я думаю, что самый простой способ - использовать awk . Пример:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }' bash
источник
ps | awk "\$1==$PID{print\$4}"
или (лучше)ps | awk -v"PID=$PID" '$1=PID{print$4}'
. Конечно, в Linux вы можете просто сделатьxargs -0n1 </proc/$PID/cmdline | head -n1
илиreadlink /proc/$PID/exe
, но в любом случае ...;
in{ print $4; }
? Удаление его, похоже, не повлияло на меня в Linux, мне просто любопытно, с какой цельюОбратите внимание, что эта
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
:Функции оболочки
Однако в целом на самом деле все еще можно использовать функции оболочки в сжатой форме, потому что в
read
команде есть отличная особенность :$ <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, которая по умолчанию равна пробелу, чтобы разбить строку на массив. Однако это работает только в Bash. Дэш и Эш этого не поддерживают. Мне было очень трудно разделить строку на компоненты в Busybox. Достаточно просто получить один компонент (например, используя awk), а затем повторить это для каждого необходимого параметра. Но затем вы в конечном итоге многократно вызываете awk в той же строке или многократно используете блок чтения с эхо в той же строке. Что неэффективно и не красиво. Таким образом, вы в конечном итоге разделяете, используя
${name%% *}
и так далее. Заставляет вас стремиться к некоторым навыкам Python, потому что на самом деле создание сценариев оболочки перестает быть большим удовольствием, если половина или более функций, к которым вы привыкли, исчезли. Но вы можете предположить, что даже python не был бы установлен в такой системе, и это не так ;-).источник
echo "$a"
иecho "$c"
.var=$(....... | { read a b c d; echo $c; })
. Это работает только для одной (строки), хотя в Bash вы можете разбить ее на массив, используяar=($var)
пытаться
ps |& while read -p first second third fourth etc ; do if [[ $first == '11383' ]] then echo got: $fourth fi done
источник
Использование переменных массива
set $(ps | egrep "^11383 "); echo $4
или
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
источник
Аналогично решению brianegge awk, вот его эквивалент на Perl:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
включает режим автоматического разделения, который заполняет@F
массив данными столбца.Используйте,
-F,
если ваши данные разделены запятыми, а не пробелами.Поле 3 печатается, поскольку Perl начинает отсчет с 0, а не с 1.
источник
Получение правильной строки (пример для строки № 6) выполняется с помощью головы и хвоста, а правильное слово (слово № 4) может быть получено с помощью awk:
command|head -n 6|tail -n 1|awk '{print $4}'
источник
awk NR=6 {print $4}
было бы немного эффективнееawk NR==6 {print $4}
* дох *Ваша команда
ps | egrep 11383 | cut -d" " -f 4
скучает,
tr -s
чтобы сжать пробелы, как объясняет раскрутка в своем ответе .Однако вы, возможно, захотите использовать
awk
, поскольку он обрабатывает все эти действия в одной команде:ps | awk '/11383/ {print $4}'
Это напечатает 4-й столбец в тех строках, которые содержат
11383
. Если вы хотите, чтобы это совпадало,11383
если оно появляется в начале строки, вы можете сказатьps | awk '/^11383/ {print $4}'
.источник
Вместо того, чтобы делать все эти greps и прочее, я бы посоветовал вам использовать возможности ps для изменения формата вывода.
Вы получаете строку cmmand процесса с указанным pid и ничего больше.
Он соответствует стандарту POSIX и может считаться переносимым.
источник
Bash
set
проанализирует весь вывод в параметрах позиции.Например, с
set $(free -h)
командойecho $7
отобразится "Mem:"источник
set $(sar -r 1 1)
;echo "${23}"
awk
это лучший способ сделать это.bash
а неawk
.