Ответ 1
Один простой способ - добавить проход tr
, чтобы выжать все повторяющиеся разделители полей:
$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 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
добавляет некоторые пробелы между вторым и третьим столбцами, чтобы сохранить некоторое сходство с таблицей, 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
выведет третий столбец. Как указано в моем комментарии...
Чтение по каналам будет выполняться в среде, которая не передает переменные вызывающему script.
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, потому что на самом деле shell-скриптинг - это не большая забава, если половина или более функций, к которым вы привыкли, исчезли. Но вы можете предположить, что даже такой питон не будет установлен на такой системе, и это не было; -).
попробовать
ps |&
while read -p first second third fourth etc ; do
if [[ $first == '11383' ]]
then
echo got: $fourth
fi
done
Подобно решению 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}'
Использование переменных массива
set $(ps | egrep "^11383 "); echo $4
или
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
Вместо того, чтобы делать все эти grep и прочее, я бы посоветовал использовать возможности ps для изменения формата вывода.
ps -o cmd= -p 12345
Вы получаете строку cmmand процесса с указанным pid и ничего больше.
Это POSIX-совместимый и может считаться портативным.
Ваша команда
ps | egrep 11383 | cut -d" " -f 4
пропускает a tr -s
, чтобы сжать пробелы, как объясняет в его ответ.
Однако вы, возможно, захотите использовать awk
, поскольку он обрабатывает все эти действия в одной команде:
ps | awk '/11383/ {print $4}'
Это печатает 4-й столбец в строках, содержащих 11383
. Если вы хотите, чтобы это соответствовало 11383
, если оно появилось в начале строки, вы можете сказать ps | awk '/^11383/ {print $4}'
.
Bash set
будет анализировать весь вывод в параметрах позиции.
Например, с помощью команды set $(free -h)
echo $7
отобразит "Mem:"