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

Я создаю скрипт 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

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

2 ответа

Чтение в файле построчно с 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)

Я думаю, вам нужно <<< до вашего пути / имени файла var после того, как сделано. Или, может быть <<< "$( cat $filename)"

Например, вот процедура для чтения вывода df -h в некоторые глобальные массивы:

declare -a FileSystem
declare -a PctUsed
declare -i Percent   #typeset Percent to get rid of warning about integer messages

get_data() {
   printf "Enter percentage threshold to report : "
   read Threshold
   i=0
   while read FS SizeGB UsedGB AvailableGB Pct MountPoint
    do
       Percent=$(echo "$Pct"|tr -d "%" )
       if [ "$Threshold"  -le  "$Percent" ]
        then
          FileSystem[$i]="$FS"
          PctUsed[$i]=$Percent
          (( i+=1 ))
       fi
    done  <<< "$( df -h )"
} #end get_data
Другие вопросы по тегам