16.2. Перенаправление для блоков кода


Блоки кода, такие как циклы while, until и for, условный оператор if/then, так же могут смешиваться с перенаправлением stdin. Даже функции могут использовать эту форму перенаправления (см. Пример 22-10). Оператор перенаправления <, в таких случаях, ставится в конце блока.

Пример 16-4. Перенаправление в цикл while

  1. #!/bin/bash
  2. if [ -z "$1" ]
  3. then
  4.   Filename=names.data       # По-умолчанию, если имя файла не задано.
  5. else
  6.   Filename=$1
  7. fi
  8. #  Конструкцию проверки выше, можно заменить следующей строкой (подстановка параметров):
  9. #+ Filename=${1:-names.data}
  10. count=0
  11. echo
  12. while [ "$name" != Smith ]  # Почему переменная $name взята в кавычки?
  13. do
  14.   read name                 # Чтение из $Filename, не со stdin.
  15.   echo $name
  16.   let "count += 1"
  17. done <"$Filename"           # Перенаправление на ввод из файла $Filename.
  18. #    ^^^^^^^^^^^^
  19. echo; echo "Имен прочитано: $count"; echo
  20. #  Обратите внимание: в некоторых старых командных интерпретаторах,
  21. #+ перенаправление в циклы приводит к запуску цикла в субоболочке (subshell).
  22. #  Таким образом, переменная $count, по окончании цикла, будет содержать 0,
  23. #  значение, записанное в нее до входа в цикл.
  24. #  Bash и ksh стремятся избежать запуска субоболочки (subshell), если это возможно,
  25. #+ так что этот сценарий, в этих оболочках, работает корректно.
  26. #
  27. # Спасибо Heiner Steven за это примечание.
  28. exit 0

Пример 16-5. Альтернативная форма перенаправления в цикле while

  1. #!/bin/bash
  2. # Это альтернативный вариант предыдущего сценария.
  3. #  Предложил: by Heiner Steven
  4. #+ для случаев, когда циклы с перенаправлением
  5. #+ запускаются в субоболочке, из-за чего переменные, устанавливаемые в цикле,
  6. #+ не сохраняют свои значения по завершении цикла.
  7. if [ -z "$1" ]
  8. then
  9.   Filename=names.data     # По-умолчанию, если имя файла не задано.
  10. else
  11.   Filename=$1
  12. fi
  13. exec 3<&0                 # Сохранить stdin в дескр. 3.
  14. exec 0<"$Filename"        # Перенаправить stdin.
  15. count=0
  16. echo
  17. while [ "$name" != Smith ]
  18. do
  19.   read name               # Прочитать с перенаправленного stdin ($Filename).
  20.   echo $name
  21.   let "count += 1"
  22. done <"$Filename"         # Цикл читает из файла $Filename.
  23. #    ^^^^^^^^^^^^
  24. exec 0<&3                 # Восстановить stdin.
  25. exec 3<&-                 # Закрыть временный дескриптор 3.
  26. echo; echo "Имен прочитано: $count"; echo
  27. exit 0

Пример 16-6. Перенаправление в цикл until

  1. #!/bin/bash
  2. # То же самое, что и в предыдущем примере, только для цикла "until".
  3. if [ -z "$1" ]
  4. then
  5.   Filename=names.data         # По-умолчанию, если файл не задан.
  6. else
  7.   Filename=$1
  8. fi
  9. # while [ "$name" != Smith ]
  10. until [ "$name" = Smith ]     # Проверка != изменена на =.
  11. do
  12.   read name                   # Чтение из $Filename, не со stdin.
  13.   echo $name
  14. done <"$Filename"             # Перенаправление на ввод из файла $Filename.
  15. #    ^^^^^^^^^^^^
  16. # Результаты получаются теми же, что и в случае с циклом "while", в предыдущем примере.
  17. exit 0

Пример 16-7. Перенаправление в цикл for

  1. #!/bin/bash
  2. if [ -z "$1" ]
  3. then
  4.   Filename=names.data          # По-умолчанию, если файл не задан.
  5. else
  6.   Filename=$1
  7. fi
  8. line_count=`wc $Filename | awk '{ print $1 }'`
  9. #           Число строк в файле.
  10. #
  11. #  Слишком запутано, тем не менее показывает
  12. #+ возможность перенаправления stdin внутри цикла "for"...
  13. #+ если вы достаточно умны.
  14. #
  15. # Более короткий вариант    line_count=$(wc < "$Filename")
  16. for name in `seq $line_count`  # "seq" выводит последовательность чисел.
  17. # while [ "$name" != Smith ]   —   более запутанно, чем в случае с циклом "while"   --
  18. do
  19.   read name                    # Чтение из файла $Filename, не со stdin.
  20.   echo $name
  21.   if [ "$name" = Smith ]
  22.   then
  23.     break
  24.   fi
  25. done <"$Filename"              # Перенаправление на ввод из файла $Filename.
  26. #    ^^^^^^^^^^^^
  27. exit 0

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

Пример 16-8. Перенаправление устройств (stdin и stdout) в цикле for

  1. #!/bin/bash
  2. if [ -z "$1" ]
  3. then
  4.   Filename=names.data          # По-умолчанию, если файл не задан.
  5. else
  6.   Filename=$1
  7. fi
  8. Savefile=$Filename.new         # Имя файла, в котором сохраняются результаты.
  9. FinalName=Jonah                # Имя, на котором завершается чтение.
  10. line_count=`wc $Filename | awk '{ print $1 }'`  # Число строк в заданном файле.
  11. for name in `seq $line_count`
  12. do
  13.   read name
  14.   echo "$name"
  15.   if [ "$name" = "$FinalName" ]
  16.   then
  17.     break
  18.   fi
  19. done < "$Filename" > "$Savefile"     # Перенаправление на ввод из файла $Filename,
  20. #    ^^^^^^^^^^^^^^^^^^^^^^^^^^^       и сохранение результатов в файле.
  21. exit 0

Пример 16-9. Перенаправление в конструкции if/then

  1. #!/bin/bash
  2. if [ -z "$1" ]
  3. then
  4.   Filename=names.data   # По-умолчанию, если файл не задан.
  5. else
  6.   Filename=$1
  7. fi
  8. TRUE=1
  9. if [ "$TRUE" ]          # конструкции "if true" и "if :" тоже вполне допустимы.
  10. then
  11.  read name
  12.  echo $name
  13. fi <"$Filename"
  14. #  ^^^^^^^^^^^^
  15. # Читает только первую строку из файла.
  16. exit 0

Пример 16-10. Файл с именами "names.data", для примеров выше

  1. Aristotle
  2. Belisarius
  3. Capablanca
  4. Euler
  5. Goethe
  6. Hamurabi
  7. Jonah
  8. Laplace
  9. Maroczy
  10. Purcell
  11. Schmidt
  12. Semmelweiss
  13. Smith
  14. Turing
  15. Venn
  16. Wilson
  17. Znosko-Borowski
  18. #  Это файл с именами для примеров
  19. #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh".

Перенаправление stdout для блока кода, может использоваться для сохранения результатов работы этого блока в файл. См. Пример 3-2.

Встроенный документ — это особая форма перенаправления для блоков кода.