Как получить первое слово вывода команды в bash?

127

У меня есть команда, например: echo "word1 word2". Я хочу поставить |вертикальную черту ( ) и получить слово 1 из команды.

echo "word1 word2" | ....

Я не знаю, что поставить после трубы.

Neuquino
источник

Ответы:

201

Awk - хороший вариант, если вам нужно иметь дело с конечными пробелами, потому что он позаботится об этом за вас:

echo "   word1  word2 " | awk '{print $1;}' # Prints "word1"

Однако Cut об этом не позаботится:

echo "  word1  word2 " | cut -f 1 -d " " # Prints nothing/whitespace

Здесь 'cut' ничего не печатает / пробел, потому что первым перед пробелом был другой пробел.

mattbh
источник
Нужна ли точка с запятой?
Элис Перселл
1
Это должен быть «ведущий» пробел (в начале строки), а не «конечный».
user202729
@AlicePurcell Я пробовал без; и это сработало для меня (MBP 10.14.2)
Samy Bencherif
1
Это не сработает, если строка, например, "firstWord, secondWord", поскольку эта команда awk разделяет пробелом
Роджер Оба
@RogerOba Это не вопрос OP, но вы можете использовать, -F","чтобы переопределить разделитель полей по умолчанию (пробел) с помощью запятой.
pjd
70

нет необходимости использовать внешние команды. Сам Bash справится с этой задачей. Предположим, что "word1 word2" вы откуда-то взяли и сохранили в переменной, например

$ string="word1 word2"
$ set -- $string
$ echo $1
word1
$ echo $2
word2

теперь вы можете присвоить $ 1 или $ 2 и т.д. другой переменной, если хотите.

ghostdog74
источник
11
+1 за использование только встроенных команд оболочки и stdin. @Matt M. --означает stdin, что $stringэто передается как stdin. stdinявляется разделенный пробелами в аргументы $1, $2, $3и т.д. - так же , как когда Bash Программа оценивает аргументы (например , проверка $1, $2и т.д.), этот подход имеет преимущество тенденции оболочки, чтобы расколоть stdinавтоматически в аргументы, устраняя необходимость awkили cut.
Калеб Сюй
3
@CalebXu Не stdin, setзадает аргументы оболочки.
Guido
9
word1=$(IFS=" " ; set -- $string ; echo $1)Настройте IFS для правильного распознавания пробелов между словами. Заключите в круглые скобки, чтобы не повредить исходное содержимое $ 1.
Стив Питчерс,
Это не работает, так как может быть расширено имя пути. Попробуйте с помощью string="*". Сюрприз.
gniourf_gniourf
32

Я думаю, что одним из эффективных способов является использование массивов bash:

array=( $string ) # do not use quotes in order to allow word expansion
echo ${array[0]}  # You can retrieve any word. Index runs from 0 to length-1

Кроме того, вы можете напрямую читать массивы в конвейере:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done
Isaías
источник
1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home
Это не работает, так как может быть расширено имя пути. Попробуйте с помощью string="*". Сюрприз.
gniourf_gniourf
Используйте whileсинтаксис, чтобы получить каждое первое слово в каждой строке. В противном случае используйте подход Boontawee Home. Также обратите внимание, что echo "${array[0]}"это цитируется для предотвращения расширения, замеченного gniourf-gniourf.
Isaías
Если вы попытаетесь получить доступ к индексу массива, превышающему количество слов, вы не получите ошибку. У вас будет пустая строчка
Дхумил Агарвал
26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Это имеет то преимущество, что не используются внешние команды и остаются неизменными переменные $ 1, $ 2 и т. Д.

Джон Мартер
источник
Оставить переменные $1, $2, …нетронутыми - чрезвычайно полезная функция для написания скриптов!
Serge Stroobandt
15

Если вы уверены, что в начале нет пробелов, вы можете использовать замену параметра bash:

$ string="word1  word2"
$ echo ${string/%\ */}
word1

Не упустите возможность избежать единого пространства. Дополнительные примеры шаблонов замены см. Здесь . Если у вас bash> 3.0, вы также можете использовать сопоставление регулярных выражений, чтобы справиться с ведущими пробелами - см. Здесь :

$ string="  word1   word2"
$ [[ ${string} =~ \ *([^\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1
dsl101
источник
11

Вы можете попробовать awk

echo "word1 word2" | awk '{ print $1 }'

С awk действительно легко выбрать любое слово, которое вам нравится ($ 1, $ 2, ...)

mfloryan
источник
11

Использование расширения параметров оболочки %% *

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

string='word1    word2'
echo ${string%% *}
word1

string='word1    word2      '
echo ${string%% *}
word1

объяснение

Знак %%означает удаление самого длинного из возможных совпадений  *(пробел, за которым следует любое количество любых других символов) в конечной части string.

Серж Строобандт
источник
9

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

1 @ mattbh's

echo "..." | awk '{print $1;}'

2 @ ghostdog74's

string="..."; set -- $string; echo $1

3 @ boontawee-home's

echo "..." | { read -a array ; echo ${array[0]} ; }

и 4 @ boontawee-home's

echo "..." | { read first _ ; echo $first ; }

Я измерил их с помощью Python timeit в сценарии Bash в терминале Zsh на macOS, используя тестовую строку из 215 5-буквенных слов. Выполняли каждое измерение пять раз (все результаты были для 100 петель, лучше всего из 3) и усредняли результаты:

method       time
--------------------------------
1. awk       9.2ms
2. set       11.6ms (1.26 * "1")
3. read -a   11.7ms (1.27 * "1")
4. read      13.6ms (1.48 * "1")

Хорошая работа, избиратели 👏 Количество голосов (на момент написания) соответствует скорости решений!

Генри
источник
Странно, что вы можете измерить 3 тире, поскольку тире не поддерживает массивы ( read -aнедействительно в тире).
gniourf_gniourf
Да, это странно. Я исключил этот вариант, проверил скорость, затем подумал: «Почему я оставил его» и добавил его. Удалив его сейчас, я могу перезапустить все позже, чтобы убедиться, что у меня не было какой-то ошибки
Генри
6
echo "word1 word2" | cut -f 1 -d " "

cut вырезает первое поле (-f 1) из списка полей, разделенных строкой "" (-d "")

lajuette
источник
это один способ, но ваш оператор cut не будет различать несколько пробелов между словами, если он захочет получить слово 2 позже
ghostdog74
да, awk-решение лучше.
Lajuette
3

read твой друг:

  • Если строка находится в переменной:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Если вы работаете в канале: первый случай: вам нужно только первое слово первой строки:

    printf '%s\n' "word1 word2" "line2" | { read -r first _; printf '%s\n' "$first"; }

    второй случай: вам нужно первое слово каждой строки:

    printf '%s\n' "word1 word2" "worda wordb" | while read -r first _; do printf '%s\n' "$first"; done

Они работают, если есть ведущие пробелы:

printf '%s\n' "   word1 word2" | { read -r first _; printf '%s\n' "$first"; }
gniourf_gniourf
источник
0

Поскольку perl включает в себя функциональность awk, это также можно решить с помощью perl:

echo " word1 word2" | perl -lane 'print $F[0]'
tssch
источник
0

Я работал со встроенным устройством, в котором не было ни Perl, ни awk, ни Python, и вместо этого делал это с помощью sed. Он поддерживает несколько пробелов перед первым словом (которое cutи bashрешения не справиться).

VARIABLE="  first_word_with_spaces_before_and_after  another_word  "
echo $VARIABLE | sed 's/ *\([^ ]*\).*/\1/'

Это было очень полезно при поиске psидентификаторов процессов с помощью grep, поскольку другие решения здесь, использующие только bash, не смогли удалить первые пробелы, которые psиспользуются для выравнивания.

Йохан Бьярехольт
источник