Разделение строки символом пробела (странная проблема с базовым именем)

Мне нужно разделить вывод из ps , который находится на расстоянии друг от друга.

 #!/bin/bash A=$(ps -r -e -o pcpu=,comm= | head -1) B=${A[0]} C=${A[1]} printf '%3s %s\n' $B $(basename $C) 

Вывод должен быть:

  42 bar 

Вместо этого я получаю:

 usage: basename ... 

Почему это не работает, и самое главное, как я могу заставить это работать?

Другие уже отметили, в чем заключается ошибка в вашем коде, и правильно предположили, что для исходных данных-заменителей лучше будет использовать массив данных, а также способы правильного разделения строки и т. Д.

Теперь, когда мы знаем, какова ваша настоящая команда, которую вы анализируете, мы можем быть немного более креативными с предложениями по улучшению.

Следующий скрипт возьмет каждую строку вывода вашей команды ps и прочитает ее как два бита, разделенных пробелом. Тело цикла выводит биты чтения различными способами:

 #!/bin/bash ps -r -e -o pcpu=,comm= | while IFS=' ' read -r pcpu comm; do printf 'pcpu=%s,\tcomm=%s,\tbasename of comm=%s\n' \ "$pcpu" "$comm" "${comm##*/}" done 

Здесь comm будет содержать все после первой последовательности пробелов в выводе ps (начальные пробелы перед первым столбцом будут обрезаны).

Очевидно, вы можете вставить свою head -n 1 как часть исходного конвейера, если хотите.

Обратите внимание, что в некоторых shellх, включая bash , цикл выполняется в подоболочке, поэтому любые созданные там переменные не будут доступны после завершения конвейера. В bash есть два решения:

  1. Включите lastpipe оболочки lastpipe в сценарии с помощью shopt -s lastpipe или
  2. Считайте данные в цикл с подстановкой процесса:

     while IFS=' ' read ... # ... done < <( ps ... ) 

Пример выполнения:

 $ bash script.sh pcpu=0.0, comm=tmux, basename of comm=tmux pcpu=0.0, comm=sh, basename of comm=sh pcpu=0.0, comm=sh, basename of comm=sh pcpu=0.0, comm=bash, basename of comm=bash pcpu=0.0, comm=bash, basename of comm=bash pcpu=0.0, comm=bash, basename of comm=bash pcpu=0.0, comm=ps, basename of comm=ps pcpu=0.0, comm=sh, basename of comm=sh 

Чтобы разделить строку на символ пробела, вы можете использовать оператор split + glob. Это вызывается при замене команды или расширении параметра, но, очевидно, не при сохранении в скалярную переменную, которая может хранить не более одного значения.

  var=$(...) 

Является скалярной переменной. Таким образом, весь вывод присваивается переменной $var , аналогично ${var[0]} . Для присвоения переменной массива это:

  var=($(...)) 

Теперь, прежде чем вызывать его, как всегда, вам нужно настроить его:

  set -o noglob # disable the glob part which you don't want here IFS=' ' # split on space only var=($(some command)) # invoke it 

Обратите внимание, что он разбивается на последовательности пробелов, а начальные и конечные пробелы игнорируются.

Затем вы не хотите вызывать его при расширении $B ни $C ни $(basename...) поэтому вам нужно заключить их в кавычки:

  printf '%3s %s\n' "$B" "$(basename -- "$C")" 

Также не забывайте -- отмечать конец опций.

Поскольку вы забыли кавычки вокруг $C (в вашем случае $C было пустым, поскольку ${A[1]} никогда не назначалось никакого значения), basename не получало ни одного аргумента вместо передачи этого пустого аргумента и жаловалось на него ,

определить массив с помощью (). Если вы используете двойные кавычки, то это будет целая строка.

Узнайте больше о массиве

 $ cat test.sh #!/bin/bash A=(42 /foo/bar) B=${A[0]} C=${A[1]} printf '%3s %s\n' $B $(basename $C) $ bash test.sh 42 bar 

Другой подход с использованием awk:

  echo "42 /foo/bar" | awk '{n=split($2,b,"/"); print $1,b[n]}'