удар:разделить вывод команды по столбцам
Вопрос
Я хочу сделать это:
- запустите команду
- захват выходных данных
- выберите строку
- выберите столбец в этой строке
Просто в качестве примера, допустим, я хочу получить имя команды из $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
Другие советы
Я думаю, что самый простой способ - использовать awk . Пример: р>
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }'
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
Функции оболочки
Однако, в общем, все еще возможно использовать функции оболочки в сжатой форме, потому что в команде 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'`
The Array Solution
Таким образом, мы в итоге получаем ответ от @frayser, который должен использовать переменную оболочки IFS, которая по умолчанию устанавливается на пробел, чтобы разбить строку на массив. Это работает только в Bash, хотя. Дэш и Эш не поддерживают это. Мне было очень трудно разделить строку на компоненты в Busybox. Достаточно просто получить один компонент (например, используя awk), а затем повторить это для каждого необходимого параметра. Но затем вы в конечном итоге неоднократно вызываете awk в одной и той же строке или постоянно используете блок чтения с echo в той же строке. Что не эффективно или красиво. Таким образом, вы заканчиваете разделение, используя $ {name %% *}
и так далее. Заставляет вас жаждать некоторых навыков Python, потому что на самом деле создание сценариев оболочки уже не доставляет большого удовольствия, если половина или более функций, к которым вы привыкли, пропали. Но вы можете предположить, что даже 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
Получение правильной строки (пример для строки № 6) выполняется с помощью головы и хвоста, а правильное слово (слово № 4) может быть записано с помощью awk:
command|head -n 6|tail -n 1|awk '{print $4}'
Использование переменных массива
set $(ps | egrep "^11383 "); echo $4
или
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
Вместо того, чтобы делать все эти операции, я бы посоветовал вам использовать возможности ps для изменения формата вывода.
ps -o cmd= -p 12345
Вы получаете строку cmmand процесса с указанным pid и ничего больше.
Это POSIX-совместимый код, поэтому его можно считать переносимым.
Ваша команда
ps | egrep 11383 | cut -d" " -f 4
пропускает tr-s
, чтобы сжать пробелы, как объясняет раскрутка в его ответе . р>
Однако вы, возможно, захотите использовать awk
, поскольку он обрабатывает все эти действия в одной команде:
ps | awk '/11383/ {print $4}'
Это печатает 4-й столбец в тех строках, которые содержат 11383
. Если вы хотите, чтобы это совпадало с 11383
, если оно появляется в начале строки, вы можете сказать ps | awk '/ ^ 11383 / {print $ 4}'
.
set
Bash будет анализировать весь вывод в параметры позиции. Р>
Например, с помощью команды set $ (free -h)
echo $ 7
покажет " Mem: "