Як отримати перше слово виводу команди в bash?


127

У мене є команда, наприклад: echo "word1 word2". Я хочу поставити pipe ( |) і отримати word1 від команди.

echo "word1 word2" | ....

Я не знаю, що поставити після труби.

Відповіді:


201

Awk - це хороший варіант, якщо вам доведеться мати справу з пробілом пробілів, оскільки він піклується про вас за вас:

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

Cut не потурбується про це, хоча:

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

"cut" тут не надрукує нічого / пробіл, тому що перше, що було перед пробілом, було іншим пробілом.


Чи потрібна напівколонка?
Аліса Перселл

1
Він повинен бути "провідним" пробілом (на початку рядка), а не "трейлінг".
користувач202729

@AlicePurcell я спробував це без; і це працювало для мене (MBP 10.14.2)
Samy Bencherif

1
Це не працює, якщо рядок є, наприклад, "firstWord, secondWord", як розділювач цієї команди awk по простору
Roger Oba

@RogerOba Це не було питанням ОП, але ви можете використовувати -F","для заміни роздільника поля за замовчуванням (пробіл) комою.
pjd

70

не потрібно використовувати зовнішні команди. Сам Баш може виконати роботу. Якщо припустити, що "word1 word2" ви дісталися звідкись і зберігаєте в змінній, наприклад

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

тепер ви можете призначити $ 1, або $ 2 і т.д. для іншої змінної, якщо хочете.


11
+1 для використання лише вбудованих оболонок та stdin. @ Matt M. --означає stdin, так $stringпередається як stdin. stdinє розділений пробілами в аргументи $1, $2, $3і т.д. - так само , як коли Bash Програма оцінює аргументи (наприклад , перевірка $1, $2і т.д.), цей підхід має перевагу тенденції оболонки, щоб розколоти stdinавтоматично в аргументи, усуваючи необхідність awkабо cut.
Калеб Сю

3
@CalebXu Not stdin, setвстановлює аргументи оболонки.
Гвідо

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

1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home

Це порушено, оскільки воно підлягає розширенню імені. Спробуйте це string="*". Сюрприз.
gniourf_gniourf

Використовуйте whileсинтаксис для отримання кожного першого слова в кожному рядку. В іншому випадку використовуйте підхід Boontawee Home. Також зауважте, що echo "${array[0]}"це було цитовано для запобігання розширення, як зауважило gniourf-gniourf.
Ісаїас

Якщо ви спробуєте отримати доступ до індексу масиву, який перевищує кількість слів, ви не отримаєте помилку. Ви просто отримаєте порожній рядок
Дхуміл Агарвал

26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Ця перевага полягає в тому, що не використовуються зовнішні команди і залишає змінними $ 1, $ 2 і т.д. недоторканими.


Залишання змінних $1, $2, …недоторканими - надзвичайно корисна функція для написання сценарію!
Серж Стройбандт

15

Якщо ви впевнені, що немає провідних пробілів, ви можете використовувати заміну параметрів bash:

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

Слідкуйте за тим, щоб уникнути єдиного простору. Тут див. Додаткові приклади моделей заміни. Якщо у вас bash> 3.0, ви також можете використовувати відповідність регулярних виразів, щоб впоратися з провідними пробілами - див. Тут :

$ string="  word1   word2"
$ [[ ${string} =~ \ *([^\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1

11

Ви можете спробувати awk

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

З awk дуже просто вибрати будь-яке вподобане слово ($ 1, $ 2, ...)


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 ; }

Я вимірював їх timeit Python у сценарії 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 вирізає 1-е поле (-f 1) зі списку полів, розділених рядком "" (-d "")


це один із способів, але ваш вирізаний виріз не розрізнятиме декілька пробілів між словами, якщо він хоче отримати слово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"; }

0

Оскільки perl містить функціональні можливості awk, це також можна вирішити за допомогою perl:

echo " word1 word2" | perl -lane 'print $F[0]'

0

Я працював із вбудованим пристроєм, який не мав ні perl, awk, ні python, і робив це замість sed. Він підтримує кілька пробілів перед першим словом (з яким cutі bashрішення не оброблялися).

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.