Чтение в файле построчно с Bash - PullRequest
0 голосов
/ 19 ноября 2018

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

export inputfilename="employees.txt"
export outputfilename="branch.txt"
directoryinput=$(find -name $inputfilename)
directoryoutput=$(find -name $outputfilename)
n=1

if [[ -f "$directoryinput" ]]; then
     while read line; do
         echo "$line"
         n=$((n+1))
     done < "$directoryoutput"
 else
    echo "Input file does not exist. Please create a employees.txt file"
 fi

Вся помощь очень ценится, спасибо!ПРИМЕЧАНИЕ: Как заметили люди, я забыл добавить знак $ на передачу данных в файл, но это было только при копировании моего кода, у меня действительно есть знак $ в моем реальном приложении, и все равно нет результата

1 Ответ

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

Чтение в файле построчно с Bash

Лучший идиоматический способ читать файл построчно:

while IFS= read -r line; do
  // parse line
  printf "%s" "$line"
done < "file"

Больше на эту тему можно найти на bashfaq

Однако не читайте файлы в bash построчно. Вы можете (хорошо, почти) всегда не читать поток построчно в bash. Строковое чтение файла в bash чрезвычайно медленное и не должно выполняться. Для простых случаев можно использовать все инструменты Unix с помощью xargs или parallel, для более сложных awk и datamesh.

done < "directoryoutput"

Код не работает, потому что вы передаете свой цикл while в качестве входных данных для стандартного ввода содержимого файла с именем directoryoutput. Так как такого файла не существует, ваш скрипт завершается ошибкой.

directoryoutput=$(find -name $outputfilename)

Можно просто добавить значение переменной с добавленной новой строкой к циклу чтения, используя конструкцию HERE-string:

done <<< "$directoryoutput"

directoryinput=$(find -name $inputfilename)
if [[ -f "$directoryinput" ]]

Это нормально, если в вашем каталоге есть только один файл с именем $inputfilename. Также нет смысла искать файл, а затем проверять его наличие. В случае большего количества файлов find возвращает список имен, разделенных новой строкой. Однако небольшой чек if [ "$(printf "$directoryinput" | wc -l)" -eq 1 ] или использование find -name $inputfilename | head -n1 я думаю, будет лучше.

while read line;
   do
      echo "$line"
      n=$((n+1))
  done < "directoryoutput"

Намерение здесь довольно ясно. Это просто:

 n=$(<directoryoutput wc -l)
 cat "directoryoutput"

За исключением того, что while read line убрал завершающие и ведущие символы новой строки и зависит от IFS.

Также всегда не забывайте указывать в кавычках ваши переменные, если у вас нет причин не делать этого.

Взгляните на shellcheck , который может найти наиболее распространенные ошибки в скриптах.

Я бы сделал это примерно так:

inputfilename="employees.txt"
outputfilename="branch.txt"

directoryinput=$(find . -name "$inputfilename")
directoryinput_cnt=$(printf "%s\n" "$directoryinput" | wc -l)
if [ "$directoryinput_cnt" -eq 0 ]; then
   echo "Input file does not exist. Please create a '$inputfilename' file" >&2
   exit 1
elif [ "$directoryinput_cnt" -gt 1 ]; then
   echo "Multiple file named '$inputfilename' exists in the current path" >&2
   exit 1
fi

directoryoutput=$(find . -name "$outputfilename")
directoryoutput_cnt=$(printf "%s\n" "$directoryoutput" | wc -l)

if [ "$directoryoutput_cnt" -eq 0 ]; then 
    echo "Input file does not exist. Please create a '$outputfilename' file" >&2
    exit 1
elif [ "$directoryoutput_cnt" -gt 1 ]; then 
   echo "Multiple file named '$outputfilename' exists in the current path" >&2
    exit 1
fi

cat "$directoryoutput"
n=$(<"$directoryoutput" wc -l)
...