Может ли bash xargs plus bash paste выводить ровно 3 файла при использовании 5 входных файлов? - PullRequest
0 голосов
/ 19 ноября 2018

Может быть, xargs может сделать это, может быть, нет, но это кажется возможным.Решение не должно использовать xargs вообще.Предпочел бы все команды Bash, но без Python.Он должен работать с огромным количеством входных файлов (здесь показан только пример размера игрушки) и, следовательно, не пытаться загрузить все содержимое файлов в память заранее.

Начальный ввод - 5 имен файловв текстовом файле 'docs.txt' все в одном столбце:

[ga@sam ~]$ cat docs.txt
a.1.txt
a.2.txt
b.1.txt
c.1.txt
c.2.txt

Требуемый вывод - ровно 3 файла: Выходной файл a.doc будет содержать содержимое a.1.txt и a.2.txt в этом порядке.Выходной файл b.doc: содержимое b.1.txt.Выходной файл c.doc: Содержимое файлов c.1.txt и c.2.txt в следующем порядке.

В настоящее время я делаю, что xargs получает 3 строки ввода, а gnu paste объединяетсодержимое файлов, перечисленных в каждой строке.Хотелось бы, чтобы xargs выводил ровно 3 текстовых файла, по одному на каждую строку ввода xargs, с именами, как показано выше, в зависимости от каждого значения группировки, как объяснено, но я не нашел хитрости.

Вот код на данный момент:

[ga@sam ~]$ cat docs.txt | awk -F. '{ORS=" "}NR==1 {prev=$1; print; next} prev!=$1{print "\n";}{prev=$1}1' | xargs -L 1 paste -s
my cat
has fleas
my dog is clean
the bat
ate a rat
[ga@sam ~]$ cat docs.txt | awk -F. '{ORS=" "}NR==1 {prev=$1; print; next} prev!=$1{print "\n";}{prev=$1}1' # | xargs -L 1 paste -s
a.1.txt a.2.txt
 b.1.txt
 c.1.txt c.2.txt [ga@sam ~]$
[ga@sam ~]$ cat docs.txt | awk -F. '{ORS=" "}NR==1 {prev=$1; print; next} prev!=$1{print "\n";}{prev=$1}1' | xargs -L 1 -P 0 --process-slot-var=f paste -s > "$f".doc
xargs: unrecognized option '--process-slot-var=f'

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

Цель вставки здесь аналогична cat.Я буду объединять файлы последовательно - это все.Если мы хотим использовать cat вместо вставки, она будет работать так же хорошо, вероятно, если она будет немного медленнее, чем вставка, и команда cat будет выглядеть так при 3 вызовах:

cat a.1.txt a.2.txt > a.doc
cat b.1.txt > b.doc
cat c.1.txt c.2.txt > c.doc

Но, как я пыталсяобъясните, я не хочу явно кодировать 3 строки cat заранее, потому что это будет динамически определенное количество выходных файлов, полностью основанное на группах, найденных во входном файле.

Даже если я обновлюсьмои xargs до последней версии, я все еще ожидаю критической неспособности произвести ровно 3 выходных файла с моим кодом, написанным как показано выше.В этом приложении xargs -process-slot-var создает несколько файлов на основе системных характеристик, а не 3, и, что более важно, количество выходных файлов напрямую зависит от количества групп, найденных в реальном приложении.

Если однострочник не сработает, я мог бы, возможно, прибегнуть к использованию некоторой циклической структуры (в awk?) Для выполнения некоторых подстановок переменных, которые в конечном итоге генерируют одну строку команды bash на выходной файл.Я не знаю достаточно хорошо, чтобы испускать команды.Если бы это было сделано таким образом, я бы предпочел, чтобы bash параллельно запускал строки параллельно, поскольку будет много миллионов выходных файлов, как описано в этом приложении.

Спасибо за идеи.

Ответы [ 4 ]

0 голосов
/ 21 ноября 2018

Следующий код - решение, которое я выбрал.Я разработал его, но не использовал чьи-либо представления кода, отчасти потому, что представления других людей еще не стали доступны во время его разработки.Спасибо за все ваши ответы и ответы и комментарии в любом случае.Код ниже работает быстро и выполняет все, что требуется.Он также не имеет явных циклов, что интересно.Возможно, вам понравится мой финальный код, поскольку многие из вас проявили интерес.С наилучшими пожеланиями.Как политика, я отказываюсь от голосов, пока никто не вносит голосов за меня (все еще ноль) за мой первоначальный вопрос, несмотря на внимание, которое он привлек;но я с радостью отдаю назад.

#!/bin/bash
# Inputs from tmp subdir
# Outputs to consolidated subdir
# Please run in dir above tmp
# No pipes allowed in an array element apparently? But PASTING worked OK, maybe since a string contains the pipe.
# The head (below) after INFILESSORT is only for dev speed.
# For dev and debugging only please remove --max-procs=0 which is for parallelism.

INFILESFIND=(find tmp -name "*.doc" -type f)
INFILESSORT=(sort -k1 -k2 -t'.')
GROUPING=(awk -F. '{ORS=" "}NR==1 {prev=$1; print; next} prev!=$1{print "\n";}{prev=$1}1')
PASTING=(xargs --max-procs=0 -L 1 -I filenames sh -c 'echo "filenames" | xargs -L 1 paste -s > consolidated/$(echo $(basename "filenames") | cut -f1 -d.).doc')
# The following line executes the script's arrays that were defined above.
"${INFILESFIND[@]}" | "${INFILESSORT[@]}" | "${GROUPING[@]}" | "${PASTING[@]}"
0 голосов
/ 19 ноября 2018

Не могли бы вы попробовать следующее решение один раз.

sort -t'.' -k1 docs.txt | awk -F'.' 'prev!=$1{close(file);file=$1".doc"} {print > file;prev=$1}'

Теперь добавляем не-лайнерную форму решения.

sort -t'.' -k1 docs.txt |
awk -F'.' '
  prev!=$1{
    close(file) 
    file=$1".doc"
  }
{
  print > file
  prev=$1
}'
0 голосов
/ 19 ноября 2018

Не проверено, но должно быть близко:

awk '
    NR==FNR { ARGV[ARGC++]=$0; next }
    FNR==1 { close(out); out=FILENAME; sub(/\..*/,".doc",out) }
    { print >> out }
' docs.txt
0 голосов
/ 19 ноября 2018

Вы можете использовать cut и sort для извлечения групп, а затем цикл while для чтения файлов группы вместе:

cut -d. -f1 docs.txt |
  sort -u |
  while read -r group; do cat "$group".*.txt > "$group".doc; done

Также обычный удар

while IFS=. read -r group rest; do
    cat "$group.$rest" >> "$group.doc"
done < docs.txt

или обычный awk

awk -F. '{
    f = $1 ".doc"
    while (( getline line < $0 ) > 0)
        print line > f
    close($0)
}' docs.txt
...