Эффективное повторное чтение (или иное повторное использование) содержимого входного файла в bash

Я хотел бы применить команды к файлам в каталоге target_dir с помощью следующего кода.

for t_file in $(find 'target_dir' -maxdepth 1 -type f);
do
  exec {fd}<'command_list.txt'
  while read -u "${fd}" eval_command
  do
    eval "${eval_command}"
  done
  exec {fd}>&-
done

Пример command_list.txt:

# command_list.txt
cat "${t_file}"

Программа загружает файл command_file.txt для каждого файла, но я ожидаю, что будет более эффективно, если я смогу переместить указатель файла обратно в первую строку файла без необходимости закрывать и открывать его между итерациями.

exec {fd}<'command_list.txt'

for t_file in $(find 'target_dir' -maxdepth 1 -type f);
do

  (move cursor of read to the first line of 'command_list.txt')

  while read -u "${fd}" eval_command
  do
    eval "${eval_command}"
  done

done

exec {fd}>&-

Возможен ли поиск указателя файла обратно в начало файла без его повторного открытия в bash?


person mora    schedule 05.10.2016    source источник
comment
for file in $(...) сам по себе содержит ошибки — см. DontReadLinesWithFor. См. UsingFind, чтобы узнать о многочисленных правильных альтернативных подходах к выполнению кода для каждого find результата.   -  person Charles Duffy    schedule 05.10.2016
comment
Когда вы говорите курсор, вы имеете в виду указатель файла?   -  person Charles Duffy    schedule 05.10.2016
comment
@Charles Duffy: Спасибо за комментарий. Я прочитаю инструкцию, которую вы рекомендовали. И я добавил воображаемый код, чтобы объяснить свой вопрос. Я надеюсь, что вам будет легко рассказать мой вопрос. Спасибо.   -  person mora    schedule 05.10.2016
comment
@Чарльз Дерффи: да, это так.   -  person mora    schedule 05.10.2016


Ответы (2)


Чтобы ответить на буквальный вопрос: вы можете искать файловый дескриптор в bash только с помощью загружаемого модуля (плагина) в оболочку, добавляя новый встроенный модуль, или запуская внешнюю программу (наследуя ваши файловые дескрипторы) и запрашивая it< /em> для выполнения операции поиска (подход, указанный в этом ответе). Однако стоимость запуска внешней программы больше, чем стоимость простого закрытия и повторного открытия вашего файла, поэтому в данном случае такой подход не имеет смысла.


Если вы хотите сохранить свой список команд, просто сделайте это — сохраните его как массив. Если под «перемещением курсора» вы имеете в виду поиск во входном файле, bash не предоставляет примитив поиска в наборе встроенных функций по умолчанию, но в любом случае в нем нет особой необходимости.

# read command_list.txt only once, storing its contents in an array
readarray -t eval_commands < command_list.txt

while IFS= read -r -d '' t_file <&3; do          # iterate over filenames from find
  for eval_command in "${eval_commands[@]}"; do  # iterate over your array
    eval "$eval_command"
  done
done 3< <(find target_dir -maxdepth 1 -type f -print0)

Кстати, если вы собираетесь передать свое имя файла в качестве аргумента команде, а не подставлять его, вы должны сделать это следующим образом:

# put the filename expansion in *single quotes*
eval "$eval_command "'"$t_file"'

...или следующим образом:

# generate a safe version of the filename
printf -v t_file_q '%q' "$t_file"

# ...and use that safe version instead of the original
eval "$eval_command $t_file_q"

Если запустить eval "$eval_command $t_file", не соблюдая ни одной из этих мер предосторожности, файл, созданный с помощью touch $'target_dir/hello world $(rm -rf $HOME)', будет очень плохой новостью.

person Charles Duffy    schedule 05.10.2016
comment
Спасибо, что сказали мне, как его кодировать. Я полагаю, вы избегаете проблемы разделения слов с помощью массива. Но мне нужно время, чтобы понять, как он выполняет ваш код. Если вы не возражаете, не могли бы вы сказать мне, какое имя ‹ ‹(...) в (сделано 3‹ ‹(найти target_dir ...)). Я хотел бы прочитать его инструкцию, но я не знаю, как искать его без его имени. Большое спасибо. - person mora; 05.10.2016
comment
Это замена процесса. См. wiki.bash-hackers.org/syntax/expansion/proc_subst или mywiki.wooledge.org/ProcessSubstitution - person Charles Duffy; 05.10.2016

find 'target_dir' -maxdepth 1 -type f -exec cat {} \;

Это приведет к тому, что каждый файл в целевом каталоге будет иметь глубину 1 папка.

person Stephan    schedule 05.10.2016
comment
Спасибо за ответ. В моем фактическом файле command_list.txt есть несколько строк. И если я могу отделить сканирование файлов и применение к ним команд, то это удобно для меня. В любом случае, спасибо, что рассказали мне о технике. - person mora; 05.10.2016
comment
Вы всегда можете сделать цикл - cat commands_list.txt | при чтении строки; найдите 'target_dir' -maxdepth 1 -type f -exec ${line} {} \; ;Выполнено - person Stephan; 05.10.2016
comment
@Stephan, это не сработает с примером, приведенным в вопросе, где строка включает расширение параметра, предназначенное для eval обработки оболочкой. - person Charles Duffy; 05.10.2016
comment
Верно, но иметь ту же переменную в списке команд, что и в скрипте, не очень эффективно. Это означает, что вы не можете изменить переменную в сценарии, если хотите, и вы повторяетесь. Ваш список команд должен быть только командой для запуска. В вашем скрипте вы представляете переменную или файл для запуска. Вам не нужно оценивать вещи...... for c in `cat command_list.txt`; do ${c} "${t_file}";done - person Stephan; 05.10.2016