Как записать новое имя файла при вводе подстановочных знаков?

Как записать новое имя файла при вводе подстановочных знаков?

У меня есть некоторый опыт работы с терминалами Inix, полученный во время научных стажировок, в которых я принимал участие. В основном я использовал несколько утилит, таких как grep, awk, и sed, но есть одна вещь, которую я уже некоторое время пытаюсь понять, и которая действительно позволила бы мне гораздо эффективнее справляться с обработкой чисел, которую мне приходится выполнять.

У меня есть скрипт run.awk, который выполняет некоторые манипуляции с большой коллекцией массивных текстовых файлов. Как есть, он возьмет файл chloride.out, извлечет из него данные и запишет chloride.cm.

Можно ли как-то заставить этот скрипт принимать *.outи записывать *.cmфайлы на основе начальной подстановочной фразы в оболочке?

Количество написанных мной скриптов для обработки больших объемов данных, которые мне приходилось выполнять более сотни итераций, просто раздражает.

В идеале я хотел бы узнать, есть ли способ сделать это для всех моих скриптов с чем-то через оболочку. Если это не может быть автоматизировано в оболочке или эквиваленте, могу ли я хотя бы автоматизировать свои awkскрипты подобным образом, как я описал?

решение1

Вы, безусловно, можете заставить awk работать с несколькими файлами с помощью подстановочных знаков. Одним из предложений было бы оставить run.awkкак общую "функцию", которая принимает один файл и создает один выходной файл, а затем вызвать ее из другого скрипта, который затем мог бы позаботиться об ассимиляции входных и выходных файлов.

Пример

Это будет скрипт Bash, мы можем назвать его awk_runner.bash.

#!/bin/bash

for ifname in *.out; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done

Образец запуска

Я создал пример каталога с несколькими тестовыми файлами.

$ touch file{1..4}.out

В результате было создано 4 файла:

$ ls -1
file1.out
file2.out
file3.out
file4.out

Теперь запускаем наш скрипт:

$ ./awk_runner.bash
IN: file1.out, OUT: file1.cm
running run.awk with file1.out & file1.cm

IN: file2.out, OUT: file2.cm
running run.awk with file2.out & file2.cm

IN: file3.out, OUT: file3.cm
running run.awk with file3.out & file3.cm

IN: file4.out, OUT: file4.cm
running run.awk with file4.out & file4.cm

После каждой строки, начинающейся с «running...», наш скрипт может быть запущен отсюда.

Файлы в списке

Допустим, вместо использования подстановочного знака *.outу нас есть файл со списком имен файлов, например:

$ cat filelist.txt 
file1.out
file2.out
file3.out
file4.out

Мы могли бы использовать эту измененную версию нашего скрипта, которая использовала бы whileцикл вместо forцикла. Теперь давайте назовем этот вариант скрипта, awk_file_runner.bash:

#!/bin/bash

while read ifname; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done < filelist.txt

Эта версия скрипта считывает входные данные из файла filelist.txt:

done < filelist.txt

Затем для каждого витка цикла whileмы используем readкоманду для считывания строки из входного файла.

while read ifname; do

Затем он выполняет все действия так же, как и первый скрипт, запуская скрипт awkпо run.awkмере прохождения каждой строки файла.

решение2

Вместо того, чтобы писать оболочку оболочки и порождать новый экземпляр awk для каждого обрабатываемого файла, вы можете сделать это в awk напрямую. Если у вас уже есть скрипт awk, вы можете получить доступ к текущему файлу с помощью переменной FILENAME. Таким образом, если вы запустите awk 'some commands' file1 file2, вы сможете определить, работаете ли вы с file1 или file2, с помощью FILENAME. Вы также можете использовать >on print/ printfin awk. Таким образом, если у вас есть скрипт awk вроде

/pattern/{ print $1,$3 }

вы могли бы легко сделать

/pattern/{ print $1,$3 > FILENAME".processed" }

или использовать FNR=1, чтобы определить, когда вы находитесь в новом файле, и создать переменную для выполнения более сложных манипуляций с именем файла. Например, заменить расширение .inна .out, как в

sauer@humpy:/tmp$ grep . file*.in
file1.in:a
file1.in:b
file2.in:c
sauer@humpy:/tmp$ awk 'FNR=1{out=FILENAME;sub("\.in$",".out",out)} {print "processed"$0 > out}' file*.in
sauer@humpy:/tmp$ grep . file*.out
file1.out:processeda
file1.out:processedb
file2.out:processedc

Я использую grep .для отображения имени файла и содержимого нескольких файлов здесь, что также является забавным трюком. Но важно установить значение переменной outв измененную версию, FILENAMEкогда FNRизменяется на 1 (так что мы находимся на строке 1 файла), а затем перенаправить все отпечатки в out. Обратите внимание, что это немного опасно, поскольку несоответствие расширения приведет к отсутствию замены, что приведет к перезаписи ваших входных файлов. Поэтому было бы неплохо добавить отказоустойчивую проверку, чтобы убедиться в этом out != FILENAMEили что-то в этом роде. Это оставлено в качестве упражнения для читателя. ;)

Если вам нужен файл, содержащий список имен файлов, проще всего запустить его так:

awkscript $(< /path/to/filename_list_file )

Который берет содержимое filename_list_fileи помещает его в командную строку.

Связанный контент