Каковы операторы управления и перенаправления оболочки?

249

Я часто вижу онлайн-уроки, которые соединяют различные команды с разными символами. Например:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2

Другие, кажется, соединяют команды с файлами:

command1  > file1
command1  >> file1

Что это за вещи? Как они называются? Что они делают? Их больше?


Мета нить об этом вопросе. ,

Тердон
источник

Ответы:

341

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

А. Операторы управления

На языке команд оболочки - токен, который выполняет функцию управления.
Это один из следующих символов:

&   &&   (   )   ;   ;;   <newline>   |   ||

И |&в баш.

!Это не оператор управления , но зарезервированное слово . Это становится логическим НЕ [оператором отрицания] внутри арифметических выражений и внутри тестовых конструкций (хотя все еще требуется разделитель пробелов).

A.1 Список терминаторов

  • ; : Выполняет одну команду за другой, независимо от результата первой.

    command1 ; command2

    Сначала command1запускается, на переднем плане, и как только он закончится, command2будет запущен.

    Новая строка, которая отсутствует в строковом литерале или после определенных ключевых слов, не эквивалентна точке с запятой. Список ;простых команд с разделителями по-прежнему является списком - так как в синтаксическом анализаторе оболочки все равно необходимо продолжить чтение простых команд, которые следуют за ;простой командой с разделителями перед выполнением, тогда как символ новой строки может разделять весь список команд - или список списков. Разница невелика, но сложна: поскольку оболочка не имеет предыдущего императива для чтения данных после новой строки, новая строка отмечает точку, в которой оболочка может начинать вычислять простые команды, которые она уже прочитала, тогда как точка с запятой ;делает не.

  • & : Это запустит команду в фоновом режиме, что позволит вам продолжить работу в той же оболочке.

     command1 & command2

    Здесь command1он запускается в фоновом режиме и сразу же command2начинает работать на переднем плане, не дожидаясь command1выхода.

    Новая строка после command1необязательна.

А.2 Логические операторы

  • && : Используется для создания списков AND, позволяет выполнять одну команду, только если другая успешно завершена.

     command1 && command2

    Здесь command2будет выполняться после command1завершения и только в случае command1успеха (если его код выхода был 0). Обе команды выполняются на переднем плане.

    Эта команда также может быть написана

    if command1
    then command2
    else false
    fi

    или просто, if command1; then command2; fiесли статус возврата игнорируется.

  • || : Используется для создания списков ИЛИ, позволяет выполнять одну команду, только если другая не удалась.

     command1 || command2

    Здесь command2будет работать только в случае command1неудачи (если он вернул состояние выхода, отличное от 0). Обе команды выполняются на переднем плане.

    Эта команда также может быть написана

    if command1
    then true
    else command2
    fi

    или короче if ! command1; then command2; fi.

    Обратите внимание, что &&и ||левоассоциативны; см. Приоритет логических операторов оболочки &&, || для дополнительной информации.

  • !: Это зарезервированное слово, которое действует как оператор «не» (но должен иметь разделитель), используется для отмены возвращаемого состояния команды - возвращает 0, если команда возвращает ненулевой статус, возвращает 1, если возвращает состояние 0 Также логическое НЕ для testутилиты.

    ! command1
    
    [ ! a = a ]

    И истинный оператор НЕ внутри арифметических выражений:

    $ echo $((!0)) $((!23))
    1 0

А.3 Трубный оператор

  • |: Оператор канала, он передает вывод одной команды как ввод другой. Команда, построенная из оператора канала, называется конвейером .

     command1 | command2

    Любой вывод, напечатанный с помощью command1, передается в качестве ввода command2.

  • |&: Это сокращение для 2>&1 |bash и zsh. Он передает как стандартный вывод, так и стандартную ошибку одной команды как ввод другой.

    command1 |& command2

А.4 Другая пунктуация списка

;;используется исключительно для обозначения конца оператора case . Ksh, bash и zsh также поддерживают переход ;&к следующему случаю и ;;&(не в ATT ksh) для продолжения и проверки последующих случаев.

(и )используются для группировки команд и запуска их в подоболочке. {а }также групповые команды, но не запускайте их в подоболочке. Смотрите этот ответ для обсуждения различных типов скобок, скобок и скобок в синтаксисе оболочки.

B. Операторы перенаправления

Оператор перенаправления

На языке команд оболочки - токен, выполняющий функцию перенаправления. Это один из следующих символов:

<     >     >|     <<     >>     <&     >&     <<-     <>

Они позволяют вам контролировать ввод и вывод ваших команд. Они могут появляться в любом месте в пределах простой команды или могут следовать за командой. Перенаправления обрабатываются в порядке их появления слева направо.

  • < : Дает ввод в команду.

    command < file.txt

    Выше будет выполнено commandна содержание file.txt.

  • <>: то же, что и выше, но файл открыт в режиме чтения + записи, а не только для чтения :

    command <> file.txt

    Если файл не существует, он будет создан.

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

  • > : Направляет вывод команды в файл.

    command > out.txt

    Выше сохранит вывод commandкак out.txt. Если файл существует, его содержимое будет перезаписано, а если он не существует, он будет создан.

    Этот оператор также часто используется, чтобы выбрать, следует ли печатать что-либо со стандартной ошибкой или стандартным выводом :

    command >out.txt 2>error.txt

    В приведенном выше примере >перенаправляет стандартный вывод и 2>перенаправляет стандартную ошибку. Вывод также может быть перенаправлен с использованием, 1>но, поскольку это значение по умолчанию, 1обычно опускается и записывается просто как >.

    Таким образом, чтобы работать commandна file.txtи сохранить свой выход в out.txtи сообщения об ошибках в error.txtУбежишь:

    command < file.txt > out.txt 2> error.txt
  • >|: Делает то же, что и >, но будет перезаписывать цель, даже если оболочка была настроена на отказ от перезаписи (с помощью set -Cили set -o noclobber).

    command >| out.txt

    Если out.txtсуществует, вывод commandзаменит его содержимое. Если он не существует, он будет создан.

  • >>: Делает то же самое >, за исключением того, что, если целевой файл существует, новые данные добавляются.

    command >> out.txt

    Если out.txtсуществует, вывод commandбудет добавлен к нему, после того, что уже есть в нем. Если он не существует, он будет создан.

  • &>, >&, >>&И &>>: (нестандартные). Перенаправить как стандартную ошибку, так и стандартный вывод, заменяя или добавляя соответственно.

    command &> out.txt

    commandБудут сохранены как стандартная ошибка, так и стандартный вывод out.txt, перезаписывая его содержимое или создавая его, если он не существует.

    command &>> out.txt

    Как и выше, за исключением того, что, если out.txtсуществует, вывод и ошибка commandбудут добавлены к нему.

    &>Вариант происходит в bashто время как >&вариант происходит от CSH (десятилетиями ранее). Они оба конфликтуют с другими операторами оболочки POSIX и не должны использоваться в переносимых shскриптах.

  • <<А вот документ. Он часто используется для печати многострочных строк.

     command << WORD
         Text
     WORD

    Здесь commandбудет принимать все, пока не найдет следующее вхождение WORD, Textв приведенном выше примере, в качестве ввода. Хотя WORDэто часто EoFили их вариации, это может быть любая буквенно-цифровая (и не только) строка, которая вам нравится. Когда WORDзаключено в кавычки, текст в этом документе обрабатывается буквально, и никаких расширений не выполняется (например, для переменных). Если оно не заключено в кавычки, переменные будут расширены. Для более подробной информации смотрите руководство по bash .

    Если вы хотите направить вывод command << WORD ... WORDнепосредственно в другую команду или команды, вы должны поместить конвейер в ту же строку, что << WORDи вы не можете поместить его после завершающего слова или в следующей строке. Например:

     command << WORD | command2 | command3...
         Text
     WORD
  • <<<Здесь строки, похожие на документы, но предназначенные для одной строки. Они существуют только в Unix-порте или rc (где он возник), zsh, в некоторых реализациях ksh, yash и bash.

    command <<< WORD

    Все, что задано как WORD, раскрывается, и его значение передается в качестве входных данных command. Это часто используется для передачи содержимого переменных в качестве ввода в команду. Например:

     $ foo="bar"
     $ sed 's/a/A/' <<< "$foo"
     bAr
     # as a short-cut for the standard:
     $ printf '%s\n' "$foo" | sed 's/a/A/'
     bAr
     # or
     sed 's/a/A/' << EOF
     $foo
     EOF

Несколько других операторов ( >&-, x>&y x<&y) могут быть использованы для закрытия или дублирования файловых дескрипторов. Подробнее о них см. В соответствующем разделе руководства по вашей оболочке ( здесь, например, для bash).

Это касается только наиболее распространенных операторов оболочек типа Борна. У некоторых оболочек есть несколько дополнительных операторов перенаправления.

Ksh, bash и zsh также имеют конструкции <(…), >(…)и =(…)(только последняя zsh). Это не перенаправления, а подстановка процесса .

terdon
источник
2
Вероятно, стоило бы отметить, что не все оболочки равны, и особо выделить особенности bash.
Грег Хьюгилл
1
@GregHewgill да, я вырвался из этого, сказав, что я обсуждаю в отношении bash. Это готовится как канонический Q & A, чтобы закрыть различные вопросы "Что делает эта странная штука", и большинство из них от пользователей bash. Я надеюсь, что кто-то другой подойдет и ответит за не-bash-оболочки, но выделение bash-специфических имеет большой смысл. Я должен проверить, хотя, я не знаю, какие они находятся на моей голове.
Тердон
&>, >>>и <<<все не являются posix, так как это ссылка на не только не алфавитные символы в имени здесь-документа. В этом ответе также очень мало говорится о том, как они работают - например, почти бесполезно говорить о простой команде и команде, не объясняя, что это такое и как решает оболочка.
mikeserv
@mikeserv спасибо. Они работают на Bash и Zsh, хотя. Я не знаю, что, во всяком случае, действительно связано с bash в этом списке. Я должен пройти через это и добавить снаряды, в которых каждый работает, но это должно было бы выяснить в первую очередь.
Тердон
1
@ Arc676 Нет, они не оценивают как true или false, это совершенно другой контекст. Это просто означает, что значение выхода, отличное от 0, указывает на проблему (нет false), а код выхода 0 указывает на успех (нет true). Это всегда было так и вполне стандартно. Код выхода, отличный от 0, указывает на ошибку во всех известных мне средах.
Тердон
62

Предупреждение относительно «>»

Новички Unix, которые только что узнали о перенаправлении ввода / вывода ( <и >), часто пробуют такие вещи, как

командавходной_файл > the_same_file

или же

команда ... < файл      > the_same_file

или, почти эквивалентно,

кошачий файл | команда ...> the_same_file

( grep, sed, cut, sort, И spellпримеры команд , которые люди склонны использовать в конструкциях , как эти.) Пользователи удивлены, обнаружив , что эти сценарии приводят к файлу становятся пустым.

Нюанс, который, кажется, не упоминается в другом ответе, можно найти в первом предложении раздела перенаправления bash (1) :

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

Первые пять слов должны быть выделены жирным шрифтом, курсивом, подчеркнуты, увеличены, мигать, окрашены в красный цвет и отмечены восклицательный знак в красном треугольникезначком, чтобы подчеркнуть тот факт, что оболочка выполняет запрошенное перенаправление (я) перед выполнением команды . И помни также

Перенаправление вывода приводит к открытию файла… для записи…. Если файл не существует, он создается; если он существует, он усекается до нулевого размера.

  1. Итак, в этом примере:

    sort roster > roster

    оболочка открывает rosterфайл для записи, обрезая его (т. е. отбрасывая все его содержимое), до sortзапуска программы. Естественно, ничего не поделаешь для восстановления данных.

  2. Можно наивно ожидать, что

    tr "[:upper:]" "[:lower:]" < poem > poem

    может быть лучше. Поскольку оболочка обрабатывает перенаправления слева направо, она открывается poemдля чтения (для trстандартного ввода в России), прежде чем открывает ее для записи (для стандартного вывода). Но это не помогает. Несмотря на то, что эта последовательность операций дает два дескриптора файла, они оба указывают на один и тот же файл. Когда оболочка открывает файл для чтения, содержимое все еще там, но оно все еще остается забитым до выполнения программы. 

Итак, что с этим делать?

Решения включают в себя:

  • Проверьте, имеет ли ваша программа внутреннюю возможность указать, куда выводится результат. На это часто указывает токен -o(или --output=). В частности,

    sort roster -o roster

    примерно эквивалентно

    sort roster > roster

    за исключением того, что в первом случае sortпрограмма открывает выходной файл. И это не достаточно , чтобы открыть выходной файл умного , пока после того, как она прочитала все входной файл (ов).

    Точно так же, по крайней мере, в некоторых версиях sedесть опция -i(edit in n place), которую можно использовать для записи вывода обратно во входной файл (опять же, после того , как все входные данные были прочитаны). Редакторы как ed/ ex, emacs, picoи vi/ vim разрешить пользователю редактировать текстовый файл и сохранить отредактированный текст в исходном файле. Обратите внимание, что ed(по крайней мере) можно использовать не в интерактивном режиме.

    • viимеет связанную особенность. Если вы напечатаете , он запишет содержимое буфера редактирования в , прочитает вывод и вставит его в буфер (заменяя исходное содержимое).:%!commandEntercommand
  • Простой, но эффективный:

    командавходной_файл > временный_файл   && mv временный_файл  входной_файл

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

    Варианты:

    • commandinput_file > temp_file && cp temp_file input_file && rm temp_file
      который все еще (потенциально) оставит temp_fileчитабельный мир. Даже лучше:
    • cp input_file temp_file && commandtemp_file > input_file && rm temp_file
      Они сохраняют статус ссылки, владельца и режим (защиту) файла, потенциально за счет вдвое большего количества операций ввода-вывода. (Возможно, вам придется использовать опцию вроде -aили -pon, cp чтобы сказать ей, чтобы сохранить атрибуты.)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_file temp_file &&
      mv temp_file input_file
      (разбито на отдельные строки только для удобства чтения) Это сохраняет режим файла (и, если вы root, владелец), но делает его принадлежащим вам (если вы не root) и делает его новым, отдельный файл.
  • Этот блог (редактирование файлов на месте) предлагает и объясняет

    {rm input_file   &&   command …> input_file ; } < input_file

    Это требует, чтобы commandбыла возможность обрабатывать стандартный ввод (но почти все фильтры могут). Сам блог называет это рискованным препятствием и препятствует его использованию. И это также создаст новый отдельный файл (не связанный ни с чем), принадлежащий вам и с разрешениями по умолчанию.

  • Пакет moreutils имеет команду под названием sponge:

    командаinput_file | губка the_same_file

    Смотрите этот ответ для получения дополнительной информации.

Вот что удивило меня: syntaxerror говорит :

[Большинство из этих решений] завершатся с ошибкой в ​​файловой системе только для чтения, где «только для чтения» означает, что вы $HOME будете доступны для записи, но /tmpтолько для чтения (по умолчанию). Например, если у вас Ubuntu и вы загрузились в консоль восстановления, это обычно так. Кроме того , оператор здесь-документ <<<не будет работать там , либо, так как она требует , /tmpчтобы быть для чтения / записи , так как он будет писать временный файл в там.
(ср. этот вопрос включает в себя и straceвывод)

Следующее может работать в этом случае:

  • Для продвинутых пользователей: Если ваша команда гарантированно произвести такое же количество выходных данных , как есть вход (например, sortили tr без-d или -sопции), вы можете попробовать
    командаinput_file | dd of = the_same_file conv = notrunc
    См. Этот ответ и этот ответ для получения дополнительной информации, включая объяснение вышеизложенного, и альтернативы, которые работают, если ваша команда гарантирует получение того же количества выходных данных, что и вводимых данных, или меньше (например grep, или cut). Преимущество этих ответов заключается в том, что они не требуют свободного места (или требуют совсем немного). Ответы, приведенные выше в форме, явно требуют наличия достаточного свободного места, чтобы система могла одновременно хранить весь входной (старый) файл и выходной (новый) файл; это не очевидно верно и для большинства других решений (например, и ). Исключение: вероятно, потребуется много свободного места, потому чтоcommandinput_file > temp_file && …sed -ispongesort … | dd …sort ему нужно прочитать все входные данные, прежде чем он сможет записать какой-либо вывод, и он, вероятно, буферизует большую часть, если не все эти данные, во временном файле.
  • Только для опытных пользователей:
    командавходной_файл 1 <> the_same_file
    может быть эквивалентен ddответу выше. Синтаксис открывает файл с именем файла с дескриптором для ввода и вывода , без усечения его - своего рода комбинации и . Примечание. Некоторые программы (например, и ) могут отказать в запуске в этом сценарии, поскольку они могут обнаружить, что вход и выход - это один и тот же файл. См. Этот ответ для обсуждения вышеизложенного и сценария, который заставляет этот ответ работать, если ваша команда гарантированно выдаст то же количество выходных данных, что и вводимые данные, или меньше . Предупреждение: я не проверял сценарий Питера, поэтому я не ручаюсь за него.n<> filen n<n>catgrep

Итак, в чем был вопрос?

Это была популярная тема на U & L; он адресован в следующих вопросах:

… И это не считая Super User или Ask Ubuntu. Я включил много информации из ответов на вышеупомянутые вопросы здесь в этом ответе, но не все. (То есть, для получения дополнительной информации, прочитайте перечисленные выше вопросы и ответы на них.)

PS У меня нет связи с блогом, который я цитировал выше.

Скотт
источник
Поскольку этот вопрос продолжает возникать, я подумал, что попробую написать «канонический ответ». Должен ли я опубликовать его здесь (и, возможно, дать ссылку на него из некоторых других наиболее часто задаваемых вопросов), или мне следует перенести его на один из вопросов, который фактически поднимает эту проблему? Кроме того, возможно, это ситуация, когда вопросы должны быть объединены?
Скотт
/ tmp Каталог, доступный для приложений, которым требуется место для создания временных файлов. Приложениям разрешается создавать файлы в этом каталоге, но не следует предполагать, что такие файлы сохраняются между вызовами приложения.
mikeserv
@mikeserv: Да, (1) я цитирую синтаксическую ошибку, и (2) я сказал, что был удивлен. Я думал, что если что-то будет читать-писать, так и будет /tmp.
Скотт
Что ж, сказанное @syntaxerror вдвойне странно, потому что, как мне кажется, dashэто будет оболочка восстановления по умолчанию в Ubuntu, и она не только не понимает <<<Herestring, но также получает анонимные каналы для <<heredocuments и не связывается с ${TMPDIR:-/tmp}этим. цель вообще. Смотрите это или это для демонстраций по обработке документов здесь. Кроме того, почему столько же выхода или меньше предупреждений?
mikeserv
@mikeserv: Ну dd … conv=notruncи 1<>ответы никогда не усекают выходной файл, поэтому, если выходные данные команды меньше входных (например, grep), в конце файла останутся некоторые байты оригинала. И, если на выходе больше , чем на входе (например, cat -n, nlили (потенциально) grep -n), есть риск перезаписи старых данных , прежде чем вы читали его.
Скотт
29

Дополнительные замечания по ;, &, (и)

  • Обратите внимание, что некоторые команды в ответе Тердона могут быть нулевыми. Например, вы можете сказать

    command1 ;

    (без command2) Это эквивалентно

    command1

    (т. е. он просто работает command1на переднем плане и ждет его завершения.

    command1 &

    (без no command2) запустится command1в фоновом режиме и сразу же выдаст другое приглашение оболочки.

  • В отличие от этого command1 &&, command1 ||и command1 |не имеет никакого смысла. Если вы введете один из них, оболочка (вероятно) предположит, что команда продолжается в другой строке. Он отобразит подсказку вторичной (продолжающейся) оболочки, которая обычно установлена >, и продолжит чтение. В сценарии оболочки он просто прочитает следующую строку и добавит ее к тому, что уже прочитал. (Осторожно: это может быть не тем, что вы хотите.)

    Примечание: некоторые версии некоторых оболочек могут рассматривать такие неполные команды как ошибки. В таких случаях (или, фактически, в любом случае, когда у вас длинная команда), вы можете поставить обратную косую черту ( \) в конце строки, чтобы указать оболочке продолжить чтение команды в другой строке:

    command1  &&  \
    command2

    или же

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
  • Как говорит Тердон, (и )может быть использован для группировки команд. Утверждение, что они «на самом деле не имеют отношения к этой дискуссии», является дискуссионным. Некоторые из команд в ответе Тердона могут быть группами команд . Например,

    ( command1 ; command2 )  &&  ( command3; command4 )

    Является ли это:

    • Беги command1и жди, пока он закончится.
    • Затем, независимо от результата выполнения этой первой команды, запустите command2и дождитесь ее завершения.
    • Тогда, если command2получится,

      • Беги command3и жди, пока он закончится.
      • Затем, независимо от результата выполнения этой команды, запустите command4и дождитесь ее завершения.

      Если command2не удалось, остановите обработку командной строки.

  • Вне скобок, |связывает очень плотно, так

    command1 | command2 || command3

    эквивалентно

    ( command1 | command2 )  ||  command3

    и &&и ||крепче ;, так

    command1 && command2 ; command3

    эквивалентно

    ( command1 && command2 ) ;  command3

    т.е. command3будет выполняться независимо от состояния выхода command1и / или command2.

G-Man
источник
Отлично, +1! Я сказал, что они не имеют отношения, потому что я не хотел вдаваться в подробности. Я хотел получить ответ, который мог бы послужить кратким описанием для новичков, которым интересно, что это за странные загадки в конце различных команд. Я не хотел сказать, что они бесполезны. Спасибо за добавление всего этого.
Terdon
1
Я обеспокоен проблемой «критической массы» - если мы опубликуем все, что мы можем сказать о оболочках, мы получим собственную версию TL; DR Справочного руководства Bash.
G-Man
Также стоит упомянуть: в отличие от языков семейства C, ;сам по себе (или без предшествующей ему команды) это синтаксическая ошибка, а не пустое выражение. Таким образом ; ;, это ошибка. (Распространенная ошибка для новых пользователей, ИМХО). Также: ;;это специальный разделитель, для caseвыписок.
Муру
1
@ Муру: Хороший вопрос, но давайте обобщим это. Любой из операторов управления , которые могут возникнуть между командами: ;, &&, ||, &, и |, ошибки , если они появляются ни с чем перед ними. Кроме того, Тердон обратился ;;(кратко) в своем ответе.
G-Man
1
@Wildcard: хорошо, я вижу, откуда ты. Ключевое слово «может»; все, что я говорил, было то, что я не гарантирую, что все оболочки будут принимать такие конструкции (то есть YMMV). Очевидно, я написал это прежде, чем узнал об использовании linebreakтокена в грамматике оболочки POSIX. Поэтому, возможно, можно с уверенностью сказать, что все POSIX-совместимые оболочки примут их. Я поддерживаю свое заявление как общий отказ от ответственности; если вы найдете достаточно старую оболочку pre-POSIX, такую ​​как настоящая оболочка Bourne или более старая, все ставки отменяются.
G-Man