Как я могу применить `cut` к нескольким файлам, а затем« вставить »результаты?

Я часто делаю операции вроде

paste <(cut -d, -f1 file1.csv) <(cut -d, -f1 file2.csv) 

который очень утомительный с более чем несколькими файлами.

Могу ли я автоматизировать этот процесс, например, с помощью globbing? Я могу сохранить результаты cut с помощью

 typeset -A cut_results for f in file*.csv; do cut_results[$f]="$(cut -d, -f1 $f)" done 

но я не уверен, как исходить оттуда.

7 Solutions collect form web for “Как я могу применить `cut` к нескольким файлам, а затем« вставить »результаты?”

Вы можете автоматизировать это с помощью globbing, в частности, для определения e glob , плюс eval , но это некрасиво, и цитата сложна:

 eval paste *.csv(e\''REPLY="<(cut -d, -f1 $REPLY)"'\') 
  • Часть между \'…\' – это некоторый код для выполнения для каждого соответствия шара. Он выполняется с переменной REPLY установленной в соответствие, и может ее модифицировать.
  • Я помещаю код в одинарные кавычки, чтобы он не расширялся при анализе glob.
  • Код REPLY="<(cut -d, -f1 $REPLY)" генерирует строку <(cut -d, -f1 file1.csv) если совпадение – file1.csv . Двойные кавычки необходимы, чтобы часть после знака равенства не расширялась при выполнении кода e кроме подстановки значения REPLY .
  • Поскольку каждый файл globbed заменяется на строку,

Было бы лучше скрыть сложность функции. Минимально проверены.

 function map { emulate -LR zsh local cmd pre cmd=() while [[ $# -ne 0 && $1 != "--" ]]; do cmd+=($1) shift done if ((!$#)); then echo >&2 "Usage: $0: COMMAND [ARGS...] -- PREPROCESSOR [ARGS...] -- FILES..." return 125 fi shift while [[ $# -ne 0 && $1 != "--" ]]; do pre+="${(q)1} " shift done if ((!$#)); then echo >&2 "Usage: $0: COMMAND [ARGS...] -- PREPROCESSOR [ARGS...] -- FILES..." return 125 fi shift eval "${(@q)cmd}" "<($pre${(@q)^@})" } 

Пример использования (синтаксис напоминает zargs ):

 map paste -- cut -d, -f1 -- *.csv 

Попробовать awk

 awk '{L[FNR]=L[FNR] $1 "\t"}END{for(i=1;i<=FNR;i++)print L[i]}' *.csv 

или вставить с помощью sed

 paste *.csv | sed 's/ [^\t]*//g' 

Я думаю, что ваша первая строка примерно так же хороша, как и для простого однострочного.

Если есть множество файлов со всеми разными именами, вы можете уменьшить повторяющуюся типизацию с помощью простого расширения истории «чит»:

Первый запуск <(cut -d, -f1

Обратите внимание на конечное пространство. Также обратите внимание, что эта команда даст вам дополнительную подсказку; просто нажмите Ctrl- C . Единственный момент – добавить его в историю.

Далее запустите paste !!file1.csv) !!file2.csv)

!! будет расширяться до полного содержимого предыдущего запуска команды, включая конечное пространство. Обратите внимание: если вы забудете закрывающиеся круглые скобки, вы получите вторичное приглашение; вы можете просто набрать Ctrl- C и повторить попытку, если это произойдет.

Это немного взломано, но достаточно хорошо для одноразового использования. Если вы делаете это много, вы можете написать функцию bash.

Я bash скрипты bash в настоящий момент, и это казалось отличной простой задачей для практики, поэтому я написал следующее. (Мой другой ответ дает простой взлом для расширения истории, но это полный скрипт, и я счел его достойным получения дополнительного ответа.) Я считаю, что это совместимо с POSIX и должно работать с #!/bin/sh , но не на 100% конечно. EDIT: На самом деле, =~ не совместим с POSIX. Вы могли бы взять этот чек и позволить cut вернуть ошибку.

 #!/bin/bash fieldtocut=1 delimiter=',' usage () { cat << EOF usage: $0 [-f FIELD] [-d DELIMITER] file1.. Cuts field FIELD from each file and pastes it. Default field is 1, default delimiter is ',' EOF exit $1 } while getopts ':f:d:' opt ; do case $opt in f) if [[ $OPTARG =~ ^[0-9]+$ ]] ; then fieldtocut="$OPTARG" else usage 1 fi ;; d) delimiter=$OPTARG ;; *) usage 1 ;; esac done shift $((OPTIND-1)) [ $# -eq 0 ] && usage 0 pasteargs='' for file in "$@" ; do pasteargs=$(printf '%s' "$pasteargs" '<(cut -d$delimiter -f$fieldtocut ' "$file" ') ') done eval paste $pasteargs 

Предполагая, что ваши аргументы находятся в "$@" , я верю что-то вроде:

 eval "paste $(printf "<( cut -d, -f1 %q ) " "$@")" 

должен это сделать.

Вот еще один способ сделать это, что очень похоже на ответ от Wildcard :

 files=( file1.csv file2.csv) eval paste "<( cut -d, -f1 ${^files[@]} )" 

Вместо цикла for используется расширение ${^ ... } которое является Zsh-специфичным.

Сначала должны быть назначены files причин, так что globbing всегда выполняется последним, поэтому если files необходимо сгенерировать автоматически (как в files=( *.csv ) ), то что-то вроде ${^:-( *.csv )} будет расширяться только после того, как все другие расширения произошли. Мы хотим, чтобы он расширился первым .

Расширение ${^ ... } заставляет результирующий массив действовать как результат расширения скобки. Например, присвойте x=(ab) а затем сравните 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 , но, очевидно, это не дело. Я также хочу, чтобы был способ заставить глотать, но опять же это кажется невозможным.

Вы можете получить awk чтобы перебирать файлы в lockstep и сообщать интересующее поле из каждого файла. Поместите этот код в файл, скажем cut_files.awk

 NR == FNR{printf "%s%s",$1, FS; for (k=2; k<ARGC; ++k) {getline < ARGV[k]; printf "%s%s", $1, k==ARGC-1?"\n":FS}; next}; NR != FNR{for (k=2; k<ARGC; ++k) close(ARGV[k]); exit} 

И тогда назовите его так

 awk -F',' -f cut_files.awk file1 file2 file3 file4 .... 
  • Как расширить подстроку команды?
  • Регистрация всех входных и выходных данных в zsh по требованию
  • Автозаполнение zsh без запроса
  • Все каталоги ошибочно идентифицированы как git repos в zsh
  • Параметры команды загрузки из файла верхнего уровня
  • tmux резко выходит сразу после загрузки
  • Shell, что вкладка-завершает префикс?
  • Как определить функцию, которая обрабатывает `command not found`?
  • Как изменить ключ для автозаполнения в ZSH?
  • Цветные папки в ZSH основаны на глубине каталога
  • Получение относительных связей между двумя путями
  • Отключить Oh-My-ZSH Завершение начала имени файла
  • Interesting Posts

    Извлечение содержимого zip-файла с помощью перезаписи всего режима в CentOS 5

    Поиск имени файла / местоположения скрипта bash на сервере с использованием PID

    Hotmail не получает письма от скрипта, но работает с сервера

    Тест Fail2ban-Regex всегда говорит об отсутствии хоста в группе

    AppArmor и привязки

    Могу ли я получить доступ к массиву, созданному в одном скрипте awk в последующем скрипте awk?

    Как сделать ключи доступа GPG доступными через SSH?

    Использование тире (-) вместо имени файла

    Каков наилучший подход при проверке зависимостей для сторонней библиотеки во время компиляции из источников?

    Как узнать наибольшее количество во многих документах, содержащих разные номера

    Как удалить все CRLF в файле (не заменять LF)

    Безопасность с переадресацией IP-адресов ядра включена

    Как я могу попросить NetworkManager / ModemManager запустить программу до pppd?

    Как отложить выключение с помощью systemd?

    поиск нескольких условий, а затем перенаправление вывода

    Linux и Unix - лучшая ОС в мире.