bash: кратчайший способ получить n-й столбец вывода

166

Допустим, что в течение вашего рабочего дня вы неоднократно сталкивались со следующей формой столбцового вывода какой-то команды в bash (в моем случае это выполнение svn stв моем рабочем каталоге Rails):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

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

То, что я делал, это использовал awkдля перехода ко второму столбцу, например, когда я хочу удалить все файлы (не то, что это типичный случай использования :), я бы сделал:

svn st | awk '{print $2}' | xargs rm

Поскольку я часто это печатаю, возникает естественный вопрос: есть ли более короткий (и, следовательно, более крутой) способ сделать это в bash?

ПРИМЕЧАНИЕ: я спрашиваю, по сути, вопрос о команде оболочки, хотя мой конкретный пример относится к моему рабочему процессу svn. Если вы считаете, что рабочий процесс глуп, и предлагаете альтернативный подход, я, вероятно, не буду вас опровергать, но другие могут, поскольку вопрос здесь заключается в том, как получить выходные данные команды n-го столбца в bash как можно более коротким способом. , Спасибо :)

SV1
источник
1
Когда вы часто используете команду, вам лучше создать сценарий и поместить его в свой путь. Вы можете просто создать функцию в вашем bashrc, если вам больше нравится. Я не вижу смысла в сокращении выражения выбора столбца.
Линч
1
Вы правы, и я мог бы это сделать. «Точка» - это поиск новых способов сделать что-то в bash, для целей обучения, но в основном для развлечения :)
Sv1
1
Кроме того, у вас нет своего .bashrc, когда вы куда-нибудь заходите, так что полезно обойтись без него.
Кос

Ответы:

125

Вы можете использовать cutдля доступа ко второму полю:

cut -f2

Изменить: Извините, не понял, что SVN не использует вкладки в своем выводе, так что это немного бесполезно. Вы можете настроить cutвывод, но он немного хрупкий - что-то вроде cut -c 10- бы сработает, но точное значение будет зависеть от вашей настройки.

Другой вариант что-то вроде: sed 's/.\s\+//'

Поргес
источник
1
Попробуйте использовать cut -f2с svn stвыходом. Вы увидите, что это не работает.
Линч
Я предполагал, что реальный svn stбудет использовать вкладки в своем выводе. После некоторого тестирования кажется, что это не так. Черт.
Porges
21
Передача соответствующего разделителя, чтобы -dэто работало.
Йог
6
Чтобы расширить то, что сказал @Yogh, для пробелов в качестве разделителя это будет выглядеть так cut -d" " -f2.
adamyonk
Без awk при использовании stdout xargs: svn st | XARGS | cut -d "" -f2
vr286
106

Для достижения того же, что и:

svn st | awk '{print $2}' | xargs rm

используя только bash вы можете использовать:

svn st | while read a b; do rm "$b"; done

Конечно, это не короче, но немного более эффективно и правильно обрабатывает пробелы в ваших именах файлов.

Рик Сладки
источник
Что такое aи bкак их получить?
Тимо
1
@Timo aпредставляет первый столбец и bпредставляет остальные столбцы. Если вы хотите напечатать второй столбец, используйте, read a b c;а затем используйте echoвместо rm. Я использовал это, чтобы получить кучу идентификаторов процесса, которые следовали шаблону grep, поэтому я мог прервать их все.
Мэтт Кляйнсмит
37

Я оказался в той же ситуации и в итоге добавил эти псевдонимы в мой .profileфайл:

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

Что позволяет мне писать такие вещи:

svn st | c2 | xargs rm
StackedCrooked
источник
15
Функции Bash обычно более полезны. Я сделал это: function c() { awk "{print \$$1}" } Тогда вы можете сделать: svn st | c 2 | xargs rm
Aissen
8
Но тогда мне нужно ввести лишний пробел. Это слишком утомительно :)
StackedCrooked
16

Попробуйте Zsh. Он поддерживает псевдоним суффикса, поэтому вы можете определить X в вашем .zshrc как

alias -g X="| cut -d' ' -f2"

тогда вы можете сделать:

cat file X

Вы можете сделать еще один шаг вперед и определить его для n-го столбца:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

который выведет n-й столбец файла "file". Вы можете сделать это для вывода grep или для меньшего вывода. Это очень удобная и убийственная особенность Zsh.

Вы можете пойти еще дальше и определить D следующим образом:

alias -g D="|xargs rm"

Теперь вы можете ввести:

cat file X1 D

удалить все файлы, указанные в первом столбце файла «файл».

Если вы знаете bash, zsh не является чем-то большим, за исключением некоторых новых функций.

HTH Крис

Крис
источник
ааа, я вижу, вы обновили свой ответ, когда я набирал свой комментарий выше :) Можете ли вы как-то динамически указать, какой столбец получить, или мне нужны n-строки в моем .zshrc (не то, что это важно, просто любопытно)
Sv1
Я отредактировал свой пост дальше и определил суффикс «D» для удаления файлов. Насколько я знаю, вам придется добавить строку для каждого суффикса.
Крис
Или сделайте это первым параметром команды X. Ничто из этого не требует zsh или псевдонимов, за исключением, возможно, особой идеи поместить канал в псевдоним.
Трипли
1
Я не понимаю, почему вам всем нравится этот cutвариант. Он просто не работает на моей машине с использованием вывода svn st. Попробуйте svn st | cut -d' ' -f2просто посмотреть, что получится.
Линч
@ Sv1, вы можете написать цикл для определения псевдонимов, поскольку псевдоним - это просто команда.
Ловец снов
8

Поскольку вы, кажется, не знакомы со сценариями, вот пример.

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

Если вы сохраните это ~/bin/xи убедитесь, что ~/binоно в вашем PATH(теперь это то, что вы можете и должны добавить в свой .bashrc), у вас будет кратчайшая команда для общего извлечения столбца n; х н.

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

Возможно, вы захотите расширить скрипт, чтобы разрешить другой разделитель столбцов. Awk по умолчанию анализирует ввод в поля на пробелах; использовать другой разделитель, используйте -F ':'где: новый разделитель. Реализация этого в качестве опции для скрипта делает его немного длиннее, поэтому я оставляю это как упражнение для читателя.


использование

Учитывая файл file:

1 2 3
4 5 6

Вы можете либо передать его через stdin (используя бесполезныйcat просто как заполнитель для чего-то более полезного);

$ cat file | sh script.sh 2
2
5

Или предоставьте это в качестве аргумента скрипту:

$ sh script.sh 2 file
2
5

Здесь sh script.shпредполагается, что скрипт сохраняется как script.shв текущем каталоге; если вы сохраняете его с более полезным именем где-то в вашей папке PATHи помечаете его как исполняемый, как в приведенных выше инструкциях, очевидно, используйте вместо этого полезное имя (и нет sh).

tripleee
источник
Хорошая идея, но у меня не совсем так, как на sh или bash. Это работает: #! / Bin / bash col = $ 1 shift awk "{print \ $$ col}"
mgk
Спасибо за этот поздний комментарий. Обновлено с вашим исправлением.
tripleee
1
лучшеawk -v col=$col ...
fedorqui 'ТАК прекратите вредить'
@Fedorqui Спасибо за ваш отзыв, здесь и в других местах! Обновил ответ. Теперь он немного длиннее, поэтому, если вам нужен абсолютно короткий скрипт, который вы можете иметь, посмотрите историю изменений для оригинальной версии.
tripleee
1
@Fedorqui Спасибо большое! Добавлено небольшое предостережение к catпримеру по истерическим причинам (-:
tripleee
5

Похоже, у вас уже есть решение. Чтобы упростить задачу, почему бы просто не поместить свою команду в скрипт bash (с коротким именем) и просто запустить ее вместо того, чтобы каждый раз вводить эту «длинную» команду?

Тарек Фадель
источник
Просто это не так круто, на мой взгляд. Зачем? Ну, я не хочу загружать мой .bashrc различными ярлыками, которые делают это и то, по сути, пишут мета-оболочку. Его довольно псевдоним как есть. Сказав это, ваше предложение не плохое.
Sv1
2
.bashrc? Зачем вам это загромождать не связанными с bash материалами? Я пишу отдельные сценарии для каждого «набора» команд и помещаю их в ~/scriptsкаталог. Затем добавьте все ~/scriptsв мой, PATHчтобы я мог просто называть их по имени, когда они мне нужны.
Тарек Фадель
4
Тогда не кладите это в свой .bashrc. Создайте скрипт в ~ / bin и убедитесь, что он в вашей переменной PATH.
трипл
2

Если вы в порядке с выбором столбца вручную, вы можете очень быстро использовать команду pick :

svn st | pick | xargs rm

Просто перейдите в любую ячейку 2-го столбца, нажмите, cа затем нажмитеenter

Бернардо Руфино
источник
Я опробовал инструмент «выбрать», на который вы ссылались, и мне он очень нравится. Это очень хорошо для многих ситуаций, когда я хочу напечатать выбранные столбцы, но не хочу печатать "awk" {print $ 3} '", чтобы получить нужный столбец. К сожалению, имя конфликтует с другим инструментом «pick», который кажется более популярным - его можно установить с помощью «apt install pick». Поэтому я переименовал ваш инструмент в «pickk» в моей системе и собираюсь продолжать его использовать. Спасибо за размещение ссылки.
Уильям Дай
1

Обратите внимание, что путь к файлу не обязательно должен быть во втором столбце вывода svn st. Например, если вы измените файл и измените его свойство, это будет 3-й столбец.

Смотрите возможные примеры вывода в:

svn help st

Пример вывода:

 M     wc/bar.c
A  +   wc/qax.c

Я предлагаю сократить первые 8 символов:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

Если вы хотите быть уверены на 100% и иметь дело с причудливыми именами файлов, например, с пробелами в конце, вам нужно проанализировать вывод xml:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

Конечно, вы можете захотеть использовать настоящий XML-парсер вместо grep / sed.

Михал Шрайер
источник