Вот еще один способ сделать это, очень похожий на ответ подстановочный знак :
files=( file1.csv file2.csv)
eval paste "<( cut -d, -f1 ${^files[@]} )"
Вместо для
, здесь используется расширение $ {^ ...}
, специфичное для Zsh.
Причина, по которой файлы
должны быть назначены первыми, заключается в том, что подстановка всегда выполняется в последнюю очередь, поэтому, если файлы
необходимо сгенерировать автоматически (как в files = (* .csv )
) то что-то вроде $ {^ :-( * .csv)}
будет расширяться только после того, как будут выполнены все остальные расширения. Мы хотим, чтобы он сначала расширил .
Расширение $ {^ ...}
заставляет результирующий массив действовать как результат раскрытия фигурных скобок. Например, назначьте x = (a b)
, а затем сравните echo $ {x} y
с echo $ {^ x} y
.
Цитирование необходимо для того, чтобы Zsh трактовал окружающий текст как буквальную строку. В противном случае он разделит командную строку на пробелы, поэтому наше $ {^ ...}
расширение сократится до "" $ {^ ...} ""
; то есть каждый элемент будет окружен только пустой строкой. То есть
echo "<( cut -d, -f1 ${^files[@]} )"
и
echo "<( cut -d, -f1 "\
${^files[@]}\
" )"
эквивалентны, но не то же самое, что
echo <( cut -d, -f1 ${^files[@]} )
. Но использование кавычек создает новую проблему: командная строка анализируется и разделяется без учета происходящего раскрытия. То есть, даже несмотря на то, что мы фактически ввели
paste <( cut -d, -f1 file1.csv ) <( cut -d, -f1 file2.csv )
по желанию, это фактически анализируется как
paste '<( cut -d, -f1 file1.csv )' '<( cut -d, -f1 file2.csv )'
Следовательно, нам нужно eval
, чтобы повторно проанализировать правильно сформированное выражение. Чтобы увидеть это в действии, сравните
setopt noxtrace
eval paste "<( cut -d, -f1 ${^files[@]} )" 1>/dev/null 2>&1
с
setopt xtrace
eval paste "<( cut -d, -f1 ${^files[@]} )" 1>/dev/null 2>&1
. Я надеялся, что какая-то комбинация вложенных расширений, $ {...: - ...}
, а флаги раскрытия параметров Q
, z
и / или s
приведут к переоценке без eval
, но, очевидно, это не так. Я также хотел бы, чтобы был способ принудительно использовать глобусы, но опять же это кажется невозможным.
Для этого можно использовать grep
:
grep 56896 file.txt
Если вы хотите написать сценарий, это может быть что-то вроде этого:
#!/bin/bash
inputfile='/path/to/file.txt'
grep "$1" "$inputfile"
Который вы затем запустите как:
$./script.sh 56896
Что-то вроде
grep BOOKNUMBER textfile.txt
должен решить эту проблему.
Если вы хотите, чтобы при поиске книги номер 96 также не было списка «Состояние Тома Тэтчера», вы можете использовать
grep '[[:space:]]BOOKNUMBER[[:space:]]*$' textfile.txt
вместо этого.
Как скрипт (сохранить какsearchbook
)
#!/bin/bash
BOOKFILE=/path/to/bookfile.txt
BOOK=$1
if [[ -z "$BOOK" ]]; then
echo -n "Booknumber? "
read BOOK
fi
grep '[[:space:]]'$BOOK'[[:space:]]*$' $BOOKFILE
Затем вы можете запустить
searchbook BOOKNUMBER
или
searchbook
Во втором случае вам будет предложено ввести номер.
Чтобы напечатать строки, для которых последнее слово является чем-то, на ум приходит awk
:
awk '$NF == "123"' # string comparison
awk '$NF == 123' # number comparison, would also match on 0123, 123.0, 1.23e2
awk '$NF == ENVIRON["ENVVAR"]' # number comparison if both $NF and $ENVVAR
# look like numbers, string otherwise
Для awk
эти слова по умолчанию разделены пробелами -. В зависимости от реализации пробел может быть только символом пробела или табуляции, некоторые из них могут включать другие пробелы Unicode, если они классифицируются как таковые в локали. Реализацияbusybox
awk
также включает символы вертикального интервала, включая символ возврата каретки.
Если ваш файл отформатирован с использованием разделителей строк MS -DOS, то есть последовательности символов CR LF, а не просто LF, то описанное выше не будет работать, кроме как с busybox awk. И даже с busybox awk в строках вывода по-прежнему будут те проблемные управляющие символы, которых не должно быть в Unix.
Таким образом, вы должны предварительно обработать ввод с помощью dos2unix
или d2u
, чтобы преобразовать окончания строк в формат Unix.
< file.dos dos2unix | awk '$NF == 123'
Ваш входной файл выглядит так, как будто он состоит из записей, разделенных пустыми строками. Если вы хотите напечатать полные записи для номера книги, вы можете сделать:
export BOOKNUMBER=123
< file.dos dos2unix | awk -v RS= -F '\n' '
field[split($1, field, " ")] == ENVIRON["BOOKNUMBER"]'
Там мы устанавливаем разделитель записей на пустую строку для входа в режим абзаца(записи, разделенные пустыми строками ), и разделитель полей на новую строку (, также известную как LF ), поэтому первая строка каждой записи находится в$1
(первом поле ). Мы разделяем эту первую строку специальным разделителем полей " "
, который по умолчанию является словом , разбивающимся на массив field
.А затем сравнение последнего элемента этого массива(split()
возвращает количество элементов, поэтому field[split()]
— это последний элемент )с содержимым переменной окружения $BOOKNUMBER
.
Чтобы не зацепить книгу#196
вместе с книгой #96
, я предлагаю аналогичное, но более краткое использованиеgrep
:
grep ' 96$' file.txt
Символ $
в регулярных выражениях означает «конец образца текста», поэтому он будет совпадать только в правильной строке.
Это также можно поместить в скрипт:
#!/bin/bash
grep " ${1}\$" /path/to/file.txt
Это также можно сделать более безопасно сawk
:
awk -v book=96 '$NF == book {print}' file.txt