
Я часто вижу в сети руководства, которые связывают различные команды с различными символами. Например:
command1 | command2
command1 & command2
command1 || command2
command1 && command2
Другие, похоже, связывают команды с файлами:
command1 > file1
command1 >> file1
Что это за штуки? Как они называются? Что они делают? Их еще больше?
решение1
Они называются операторами оболочки и да, их больше. Я дам краткий обзор наиболее распространенных из двух основных классов,операторы управленияиоператоры перенаправленияи как они работают по отношению к оболочке bash.
А. Операторы управления
В языке команд оболочки — токен, выполняющий функцию управления.
Это один из следующих символов:
& && ( ) ; ;; <newline> | ||
И |&
в Баше.
А !
естьнетоператор управления, ноЗарезервированное слово. Он становится логическим НЕ [оператором отрицания] внутриАрифметические выраженияи внутри тестовых конструкций (при этом все еще требуется разделитель-пробел).
A.1 Список терминаторов
;
: Выполняет одну команду после завершения другой, независимо от результата первой.command1 ; command2
Сначала command1
он запускается на переднем плане, а после завершения работы command2
будет запущен.
Перевод строки, который не находится в строковом литерале или после определенных ключевых слов,нетэквивалент оператора точки с запятой. Список ;
разделенных простых команд все еще являетсясписок- как в синтаксическом анализаторе оболочки, он должен продолжать считывать простые команды, которые следуют за ;
разделительной простой командой, перед выполнением, тогда как новая строка может ограничивать весь список команд - или список списков. Разница тонкая, но сложная: учитывая, что у оболочки нет предыдущего императива для считывания данных после новой строки, новая строка отмечает точку, в которой оболочка может начать оценивать простые команды, которые она уже считывала, тогда как точка ;
с запятой этого не делает.
&
: Это запустит команду в фоновом режиме, что позволит вам продолжить работу в той же оболочке.command1 & command2
Здесь command1
запускается в фоновом режиме и command2
начинает работать на переднем плане немедленно, не дожидаясь command1
выхода.
Перевод строки после command1
него необязателен.
A.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
A.3 Оператор трубы
|
: Оператор конвейера, он передает вывод одной команды в качестве ввода другой. Команда, созданная из оператора конвейера, называетсятрубопровод.command1 | command2
Любые выходные данные, напечатанные с помощью ,
command1
передаются в качестве входных данныхcommand2
.|&
: Это сокращение для2>&1 |
в bash и zsh. Он передает как стандартный вывод, так и стандартную ошибку одной команды в качестве ввода для другой.command1 |& command2
A.4 Другие знаки препинания в списке
;;
используется исключительно для обозначения концазаявление по делу. Ksh, bash и zsh также поддерживают ;&
переход к следующему случаю и ;;&
(не в ATT ksh) продолжение и тестирование последующих случаев.
(
и )
привыкли кгрупповые командыи запускать их в подоболочке. {
а }
также группировать команды, но не запускать их в подоболочке. Смотретьэтот ответдля обсуждения различных типов скобок, квадратных и фигурных скобок в синтаксисе оболочки.
B. Операторы перенаправления
Определение POSIX оператора перенаправления
В языке команд оболочки — токен, выполняющий функцию перенаправления. Это один из следующих символов:
< > >| << >> <& >& <<- <>
Они позволяют вам контролировать ввод и вывод ваших команд. Они могут появляться в любом месте внутри простой команды или следовать за командой. Перенаправления обрабатываются в порядке их появления, слева направо.
<
: Вводит команду.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
будет добавлен к нему, после того, что уже в нем есть. Если не существует, то будет создан.
>&
: (согласно спецификации POSIX) в окружениицифры(1>&2
) или-
с правой стороны (1>&-
) либо перенаправляет толькоодиндескриптор файла или закрывает его (>&-
).
A >&
, за которым следует номер дескриптора файла, является переносимым способом перенаправления дескриптора файла, а также >&-
переносимым способом закрытия дескриптора файла.
Если справа от этого перенаправления находится файл, пожалуйста, прочтите следующую запись.
>&
,&>
,>>&
и&>>
: (см. также выше) Перенаправить стандартную ошибку и стандартный вывод, заменив или добавив соответственно.command &> out.txt
Стандартная ошибка и стандартный вывод command
будут сохранены в out.txt
, перезаписав его содержимое или создав его, если он не существует.
command &>> out.txt
То же, что и выше, за исключением того, что если существует, то к нему будут добавлены out.txt
вывод и ошибка .command
Вариант &>
происходит из bash
, а >&
вариант происходит из csh (десятилетиями ранее). Они оба конфликтуют с другими операторами оболочки POSIX и не должны использоваться в переносимых sh
скриптах.
<<
: Документ here. Часто используется для печати многострочных строк.command << WORD Text WORD
Здесь,
command
будет принимать все, пока не найдет следующее вхождениеWORD
,Text
в примере выше, как входные данные . ХотяWORD
частоEoF
или его вариации, это может быть любая буквенно-цифровая (и не только) строка, которая вам нравится. Когда любая частьWORD
заключена в кавычки или экранирована, текст в документе here обрабатывается буквально, и никакие расширения не выполняются (например, для переменных). Если он не заключен в кавычки, переменные будут расширены. Для получения более подробной информации см.руководство по 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).
Это касается только наиболее распространенных операторов оболочек типа Bourne. Некоторые оболочки имеют несколько дополнительных собственных операторов перенаправления.
Ksh, bash и zsh также имеют конструкции <(…)
, >(…)
и =(…)
(последняя zsh
только в). Это не перенаправления, азамена процесса.
решение2
Предупреждение относительно '>'
Начинающие пользователи Unix, которые только что узнали о перенаправлении ввода-вывода ( <
и >
), часто пытаются делать что-то вроде
команда…входной_файл>тот же_файл
или
команда… <файл >тот же_файл
или, почти то же самое,
котфайл|команда… >тот же_файл
( grep
, sed
, cut
, sort
и spell
являются примерами команд, которые люди склонны использовать в подобных конструкциях.) Пользователи с удивлением обнаруживают, что эти сценарии приводят к тому, что файл становится пустым.
Нюанс, который, кажется, не упомянут в другом ответе, можно обнаружить в первом предложенииПеренаправлениеразделбаш(1):
Перед выполнением команды ее входные и выходные данные могут бытьперенаправлено с использованием специальной нотации, интерпретируемой оболочкой.
Первые пять слов должны быть выделены жирным шрифтом, курсивом, подчеркнуты, увеличены, мигать, окрашены в красный цвет и отмечены значком , чтобы подчеркнуть тот факт, что оболочка выполняет запрошенное перенаправление(я).
до выполнения команды. И помните также
Перенаправление вывода приводит к открытию файла … для записи …. Если файл не существует, он создается; если существует, он усекается до нулевого размера.
Итак, в этом примере:
sort roster > roster
оболочка открывает
roster
файл для записи, обрезая его (т.е. отбрасывая все его содержимое), прежде чемsort
программа начнет работать. Естественно, ничего нельзя сделать, чтобы восстановить данные.Можно было бы наивно ожидать, что
tr "[:upper:]" "[:lower:]" < poem > poem
может быть лучше. Поскольку оболочка обрабатывает перенаправления слева направо, она открывается
poem
для чтения (дляtr
стандартного ввода ) до того, как откроет его для записи (для стандартного вывода). Но это не помогает. Несмотря на то, что эта последовательность операций дает два дескриптора файла, они оба указывают на один и тот же файл. Когда оболочка открывает файл для чтения, его содержимое все еще там, но оно все равно затирается до того, как программа будет выполнена.
Так что же с этим делать?
Решения включают в себя:
Проверьте, имеет ли программа, которую вы запускаете, собственную внутреннюю возможность указывать, куда направляется вывод. Это часто обозначается токеном
-o
(или--output=
). В частности,sort -o roster roster
примерно эквивалентно
sort roster > roster
за исключением того, что в первом случае
sort
программа открывает выходной файл. И она достаточно умна, чтобы не открывать выходной файл, покапослеон прочитал все входные файлы.Аналогично, по крайней мере, некоторые версии
sed
имеют-i
(редактироватьяn-место) опция, которая может быть использована для записи выходных данных обратно во входной файл (опять же,послевсе входные данные были прочитаны). Редакторы, такие какed
/ex
,emacs
,pico
, иvi
/vim
позволяют пользователю редактировать текстовый файл и сохранять отредактированный текст в исходном файле. Обратите внимание, чтоed
(по крайней мере) может использоваться неинтерактивно.vi
имеет связанную функцию. Если вы введете , он запишет содержимое буфера редактирования в:%!command
Entercommand
, прочитать вывод и вставить его в буфер (заменив исходное содержимое).
Простой, но эффективный:
команда…входной_файл>временный_файл && мввременный_файл входной_файл
Это имеет тот недостаток, что если
input_file
ссылка, она (вероятно) будет заменена отдельным файлом. Кроме того, новый файл будет принадлежать вам, с защитой по умолчанию. В частности, это несет риск того, что файл станет доступным для чтения всем, даже если исходныйinput_file
не было.Вариации:
command … input_file > temp_file && cp temp_file input_file && rm temp_file
который все равно (потенциально) оставитtemp_file
Читаемый во всем мире. Еще лучше:cp input_file temp_file && command … temp_file > input_file && rm temp_file
Они сохраняют статус ссылки, владельца и режим (защиту) файла, что может привести к удвоению объема ввода-вывода. (Возможно, вам придется использовать опцию вроде-a
или-p
oncp
, чтобы указать, что атрибуты нужно сохранять.)command … input_file > temp_file &&
cp --attributes-only --preserve=all input_file temp_file &&
mv temp_file input_file
(разбито на отдельные строки только для удобства чтения) Это сохраняет режим файла (и, если вы являетесь пользователем root, владельца), но делает его владельцем вас (если вы не являетесь пользователем root) и создает новый, отдельный файл.
Этот блог («Редактирование файлов на месте») предлагает и объясняет
{ рмвходной_файл && команда… >входной_файл; } <входной_файл
Для этого необходимо, чтобы
command
иметь возможность обрабатывать стандартный ввод (но почти все фильтры могут). Сам блог называет это рискованным ляпом и не рекомендует его использовать. И это также создаст новый, отдельный файл (ни с чем не связанный), принадлежащий вам и с правами доступа по умолчанию.В пакете moreutils есть команда с названием
sponge
:команда…входной_файл| губкатот же_файл
Видетьэтот ответЧтобы получить больше информации.
Вот что стало для меня полной неожиданностью: синтаксическая ошибка говорит:
[Большинство этих решений] не будут работать в файловой системе, доступной только для чтения, где «доступ только для чтения» означает, что ваш
$HOME
волябыть доступным для записи, но/tmp
будеттолько для чтения(по умолчанию). Например, если у вас Ubuntu, и вы загрузились в Recovery Console, это обычно так. Кроме того, оператор here-document<<<
также не будет работать там, так как он требует,/tmp
чтобычитай пиши поскольку он также запишет туда временный файл.
(ср.этот вопросвключает в себяstrace
вывод 'd)
В этом случае может сработать следующее:
- Только для продвинутых пользователей:
Если ваша команда гарантированно выдаст тот же объем выходных данных, что и входные (например,
sort
илиtr
безвариант-d
или-s
), вы можете попробоватькоманда…входной_файл| дд из=тот же_файлconv=notrunc
Видетьэтот ответ иэтот ответдля получения дополнительной информации, включая объяснение вышеизложенного, а также альтернатив, которые работают, если ваша команда гарантированно выдает тот же объем выходных данных, что и входные данныеили менее(например,grep
, илиcut
). Эти ответы имеют то преимущество, что они не требуют никакого свободного места (или требуют очень мало). Ответы выше формы явно требуют, чтобы было достаточно свободного места для того, чтобы система могла одновременно хранить весь входной (старый) файл и выходной (новый) файл; это неочевидно верно и для большинства других решений (например, и ). Исключение: вероятно, потребует много свободного места, поскольку должен прочитать все свои входные данные, прежде чем сможет записать какие-либо выходные данные, и он, вероятно, буферизует большую часть, если не все эти данные, во временном файле.command … input_file > temp_file && …
sed -i
sponge
sort … | dd …
sort
- Только для продвинутых пользователей:
команда…входной_файл1<>тот же_файл
может быть эквивалентно ответуdd
выше. Синтаксис открывает указанный файл в дескрипторе файлаn<> file
n
для входа и выхода, без усечения — своего рода комбинация и . Примечание: некоторые программы (например, и ) могут отказаться запускаться в этом сценарии, поскольку они могут определить, что входные и выходные данные — это один и тот же файл. Смотритеn<
n>
cat
grep
этот ответ для обсуждения вышеизложенного и скрипта, который заставляет этот ответ работать, если ваша команда гарантированно выдает тот же объем выходных данных, что и входныхили менее.
Предупреждение: я не тестировал сценарий Питера, поэтому не ручаюсь за него.
Так в чем же был вопрос?
Это популярная тема на U&L; она рассматривается в следующих вопросах:
- Есть ли способ изменить файл на месте?
- Как заменить
iconv
входной файл преобразованным выходным файлом? - Почему команда
shuf file > file
оставляет пустой файл? - Можно ли читать и писать в один и тот же файл в Linux, не перезаписывая его?
- Перенаправить на тот же файл, что и исходный файл, обработанный командой
- Почему эта
sort
команда возвращает мне пустой файл? - Перенаправление
tr
stdout в файл - grep: входной файл «X» также является выходным
- Открывают ли операторы перенаправления файловые дескрипторы параллельно?
- Перенаправление не перезаписывает файл, а просто создает пустой
… и это не считая Super User или Ask Ubuntu. Я включил большую часть информации из ответов на вопросы выше в этот ответ, но не всю. (Т.е. для получения дополнительной информации прочтите вопросы выше и ответы на них.)
P.S. У меня естьнетпринадлежность к блогу, который я процитировал выше.
решение3
Больше наблюдений по ;
, &
, (
и)
Обратите внимание, что некоторые команды в ответе terdon могут быть нулевыми. Например, вы можете сказать
command1 ;
(без
command2
). Это эквивалентноcommand1
(т.е. он просто работает
command1
на переднем плане и ждет завершения. Для сравнения,command1 &
(без
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
Как говорит terdon,
(
и)
можно использовать для группировки команд. Утверждение, что они «не имеют особого отношения» к этой дискуссии, спорно. Некоторые из команд в ответе terdon могут быть командойгруппы. Например,( 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
.