#linux #bash
#linux #bash
Вопрос:
Представьте, что у меня есть папка foo
с файлами a
и b
(и, возможно, больше).
Я хочу перейти к paste
подстановке команд для каждого файла в foo
(здесь файлы a
и b
).
Это почти все:
paste <(cut -f1 /foo/*)
Однако это расширяется до:
paste <(cut -f1 /foo/a /foo/b)
В то время как то, что я на самом деле хочу, — это отдельная подстановка для каждого файла в foo
:
paste <(cut -f1 /foo/a) <(cut -f1 /foo/b)
Решение не обязательно должно включать подстановку команд, это просто то, как я это сформулировал.
Ответ №1:
Ваша проблема заключается в том, что <(command group)
неявно создается FIFO, но этот FIFO существует только для текущей области действия команды. Это означает, что вы не можете сохранить это FIFO в переменную и прийти позже, чтобы использовать ее поток. Он был выпущен, когда его вызывающая команда завершила выполнение.
Давайте продемонстрируем это:
echo <(:) <(:); echo <(:)
Вывод:
/dev/fd/63 /dev/fd/62
/dev/fd/63
Во втором echo
случае номер выделенного файлового дескриптора группы команд суб-оболочки сбрасывается.
Вместо этого можно явно создать именованный FIFO, поскольку они сохраняются до явного удаления, и поэтому их имя ссылочного файла может быть сохранено для последующего использования:
#!/usr/bin/env bash
tmpdir=$(mktemp -d)
trap 'rm -fr -- "$tmpdir"' EXIT
n=0
fifo_arr=()
for f in 'foo/'*; do
fifo="$tmpdir/$((n ))"
mkfifo "$fifo"
fifo_arr =("$fifo")
cut -f1 "$f" >"$fifo" amp;
done
paste "${fifo_arr[@]}"
Комментарии:
1. Это уровень волшебства, к которому я стремлюсь
Ответ №2:
Хорошо, это то, что я придумал.
ls *.txt | tr -s ' ' 'n' | sed -E 's/^(.*)$/<(cut -f1 1)/' | tr -s 'n' ' ' | awk '{ print; }' > cmd.output; sed -i -E 's/^([^s] )/paste 1/' cmd.output; bash cmd.output
У меня есть два файла в моем текущем каталоге, a.txt
и b.txt
.
Первый содержит только следующее.
a
b
c
Второй файл похож.
1
2
3
Я начинаю с того, что получаю все интересующие меня файлы. В этом примере это все *.txt
файлы.
$ ls *.txt
a.txt b.txt
Затем я разбиваю каждый из них на отдельную строку, транскрибируя каждый пробел в символ новой строки (я полагаю col
, это тоже работает).
$ ls *.txt | tr -s ' ' 'n'
a.txt
b.txt
Затем я создаю cut
команду для каждого из перечисленных файлов, используя sed
подстановку регулярных выражений.
$ ls *.txt | tr -s ' ' 'n' | sed -E 's/^(.*)$/<(cut -f1 1)'
<(cut -f1 a.txt)
<(cut -f1 b.txt)
Теперь я изменяю транскрипцию новой строки, чтобы переформатировать это как одну строку.
$ ls *.txt | tr -s ' ' 'n' | sed -E 's/^(.*)$/<(cut -f1 1)'
<(cut -f1 a.txt) <(cut -f1 b.txt)
Затем я передаю это в файл (я использовал, чтобы увидеть результаты моих экспериментов во время экспериментов). cmd.sh
tee
$ ls *.txt | tr -s ' ' 'n' | sed -E 's/^(.*)$/<(cut -f1 1)' > cmd.sh
Создав параметры команды, я затем использовал другое sed
регулярное выражение для редактирования командного файла на месте, заменив <args>
на paste <args>
.
$ sed -i -E 's/^([^s] )/paste 1/' cmd.sh
Перед:
$ cat cmd.sh
<(cut -f1 a.txt) <(cut -f1 b.txt)
После:
$ cat cmd.sh
paste <(cut -f1 a.txt) <(cut -f1 b.txt)
Наконец, я вызываю bash
, передавая имя файла в качестве аргумента.
$ bash cmd.sh
a 1
b 2
c 3
Мне любопытно посмотреть, найдет ли кто-нибудь лучшее решение. Это кажется… халтурно.