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

StackOverflow https://stackoverflow.com/questions/1629908

  •  06-07-2019
  •  | 
  •  

Вопрос

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

  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-е поле, но откуда я могу знать, особенно когда выходные данные являются переменными и неизвестны заранее.

Это было полезно?

Решение

Одним из простых способов является добавление прохода 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: "

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top