Создание подстановки команд для каждого сопоставления с шаблоном

#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
  

Мне любопытно посмотреть, найдет ли кто-нибудь лучшее решение. Это кажется… халтурно.