- Linux pipes tips & tricks
- Pipe — что это?
- Логика
- Простой дебаг
- Исходный код, уровень 1, shell
- Исходный код, уровень 2, ядро
- Tips & trics
- Что такое каналы (pipe) в Linux? Как работает перенаправление каналов?
- Каналы в Linux: общая идея
- Имейте в виду: Pipe перенаправляет стандартный вывод на стандартный ввод, но не как аргумент команды
- Типы каналов в Linux
- Каналы без названия
- Именованные каналы
- Именованные каналы не занимают память на диске
- Сниженный ввод-вывод
- Связь между двумя очень разными процессами
- Понимание каналов более низкого уровня [для опытных пользователей и разработчиков]
- Learn Linux, 101: Streams, pipes, and redirects
- Getting comfortable with Linux plumbing
- Overview
- Input, output, and streams
- Prerequisites
- Setting up the examples
- Redirecting standard IO
- Redirecting output
- Redirecting input
- Creating pipelines
- Piping stdout to stdin
- Starting pipelines with a file instead of stdout
- Using the xargs command
- Using the find command with the -exec option or with xargs
- Splitting output
Linux pipes tips & tricks
Pipe — что это?
Pipe (конвеер) – это однонаправленный канал межпроцессного взаимодействия. Термин был придуман Дугласом Макилроем для командной оболочки Unix и назван по аналогии с трубопроводом. Конвейеры чаще всего используются в shell-скриптах для связи нескольких команд путем перенаправления вывода одной команды (stdout) на вход (stdin) последующей, используя символ конвеера ‘|’:
grep выполняет регистронезависимый поиск строки “error” в файле log, но результат поиска не выводится на экран, а перенаправляется на вход (stdin) команды wc, которая в свою очередь выполняет подсчет количества строк.
Логика
Конвеер обеспечивает асинхронное выполнение команд с использованием буферизации ввода/вывода. Таким образом все команды в конвейере работают параллельно, каждая в своем процессе.
Размер буфера начиная с ядра версии 2.6.11 составляет 65536 байт (64Кб) и равен странице памяти в более старых ядрах. При попытке чтения из пустого буфера процесс чтения блокируется до появления данных. Аналогично при попытке записи в заполненный буфер процесс записи будет заблокирован до освобождения необходимого места.
Важно, что несмотря на то, что конвейер оперирует файловыми дескрипторами потоков ввода/вывода, все операции выполняются в памяти, без нагрузки на диск.
Вся информация, приведенная ниже, касается оболочки bash-4.2 и ядра 3.10.10.
Простой дебаг
Исходный код, уровень 1, shell
Т. к. лучшая документация — исходный код, обратимся к нему. Bash использует Yacc для парсинга входных команд и возвращает ‘command_connect()’, когда встречает символ ‘|’.
parse.y:
Также здесь мы видим обработку пары символов ‘|&’, что эквивалентно перенаправлению как stdout, так и stderr в конвеер. Далее обратимся к command_connect():make_cmd.c:
где connector это символ ‘|’ как int. При выполнении последовательности команд (связанных через ‘&’, ‘|’, ‘;’, и т. д.) вызывается execute_connection():execute_cmd.c:
PIPE_IN и PIPE_OUT — файловые дескрипторы, содержащие информацию о входном и выходном потоках. Они могут принимать значение NO_PIPE, которое означает, что I/O является stdin/stdout.
execute_pipeline() довольно объемная функция, имплементация которой содержится в execute_cmd.c. Мы рассмотрим наиболее интересные для нас части.
execute_cmd.c:
Таким образом, bash обрабатывает символ конвейера путем системного вызова pipe() для каждого встретившегося символа ‘|’ и выполняет каждую команду в отдельном процессе с использованием соответствующих файловых дескрипторов в качестве входного и выходного потоков.
Исходный код, уровень 2, ядро
Обратимся к коду ядра и посмотрим на имплементацию функции pipe(). В статье рассматривается ядро версии 3.10.10 stable.
fs/pipe.c (пропущены незначительные для данной статьи участки кода):
Если вы обратили внимание, в коде идет проверка на флаг O_NONBLOCK. Его можно выставить используя операцию F_SETFL в fcntl. Он отвечает за переход в режим без блокировки I/O потоков в конвеере. В этом режиме вместо блокировки процесс чтения/записи в поток будет завершаться с errno кодом EAGAIN.
Максимальный размер блока данных, который будет записан в конвейер, равен одной странице памяти (4Кб) для архитектуры arm:
arch/arm/include/asm/limits.h:
Для ядер >= 2.6.35 можно изменить размер буфера конвейера:
Максимально допустимый размер буфера, как мы видели выше, указан в файле /proc/sys/fs/pipe-max-size.
Tips & trics
В примерах ниже будем выполнять ls на существующую директорию Documents и два несуществующих файла: ./non-existent_file и. /other_non-existent_file.
Перенаправление и stdout, и stderr в pipe
или же можно использовать комбинацию символов ‘|&’ (о ней можно узнать как из документации к оболочке (man bash), так и из исходников выше, где мы разбирали Yacc парсер bash):
Перенаправление _только_ stderr в pipe
Shoot yourself in the foot
Важно соблюдать порядок перенаправления stdout и stderr. Например, комбинация ‘>/dev/null 2>&1′ перенаправит и stdout, и stderr в /dev/null.
Получение корректного кода завершения конвейра
По умолчанию, код завершения конвейера — код завершения последней команды в конвеере. Например, возьмем исходную команду, которая завершается с ненулевым кодом:
И поместим ее в pipe:
Теперь код завершения конвейера — это код завершения команды wc, т.е. 0.
Обычно же нам нужно знать, если в процессе выполнения конвейера произошла ошибка. Для этого следует выставить опцию pipefail, которая указывает оболочке, что код завершения конвейера будет совпадать с первым ненулевым кодом завершения одной из команд конвейера или же нулю в случае, если все команды завершились корректно:
Shoot yourself in the foot
Следует иметь в виду “безобидные” команды, которые могут вернуть не ноль. Это касается не только работы с конвейерами. Например, рассмотрим пример с grep:
Здесь мы печатаем все найденные строки, приписав ‘new_’ в начале каждой строки, либо не печатаем ничего, если ни одной строки нужного формата не нашлось. Проблема в том, что grep завершается с кодом 1, если не было найдено ни одного совпадения, поэтому если в нашем скрипте выставлена опция pipefail, этот пример завершится с кодом 1:
В больших скриптах со сложными конструкциями и длинными конвеерами можно упустить этот момент из виду, что может привести к некорректным результатам.
Источник
Что такое каналы (pipe) в Linux? Как работает перенаправление каналов?
Главное меню » Linux » Что такое каналы (pipe) в Linux? Как работает перенаправление каналов?
Вы, вероятно, также знаете, что такое перенаправление канала, которое используется для перенаправления вывода одной команды в качестве ввода для следующей команды.
Но знаете ли вы, что под ним? Как на самом деле работает перенаправление каналов?
Не беспокойтесь, потому что сегодня мы демистифицируем каналы Unix, чтобы в следующий раз, когда вы пойдете на свидание с этими причудливыми вертикальными полосами, вы точно знали, что происходит.
Примечание. Мы использовали термин Unix в некоторых местах, потому что концепция каналов (как и многие другие вещи в Linux) происходит от Unix.
Каналы в Linux: общая идея
Вот что вы повсюду увидите относительно «что такое каналы в Unix?»:
- Каналы Unix – это механизм IPC (межпроцессное взаимодействие), который перенаправляет вывод одной программы на вход другой программы.
Это общее объяснение, которое дают все. Мы хотим проникнуть глубже. Давайте перефразируем предыдущую строку более техническим языком, убрав абстракции:
- Каналы Unix – это механизм IPC (межпроцессного взаимодействия), который принимает программу stdout и пересылает ее другой программе stdin через буфер.
Намного лучше. Удаление абстракции сделало его намного чище и точнее. Вы можете посмотреть на следующую схему, чтобы понять, как работает pipe.
Один из простейших примеров команды pipe – передать некоторый вывод команды команде grep для поиска определенной строки.
Имейте в виду: Pipe перенаправляет стандартный вывод на стандартный ввод, но не как аргумент команды
Очень важно понять, что конвейер передает команду stdout другой команде stdin, но не как аргумент команды. Мы объясним это на примере.
Если вы используете команду cat без аргументов, по умолчанию будет выполняться чтение из stdin. Вот пример:
Здесь мы использовали cat без передачи файлов, поэтому по умолчанию stdin. Затем мы написали строку, а затем использовал Ctrl + d, чтобы сообщить, что закончили писать (Ctrl + d подразумевает EOF или конец файла). Как только мы закончили писать, cat прочитал stdin и написал эту строку в stdout.
Теперь рассмотрим следующую команду:
Вторая команда НЕ эквивалентна cat hey. Здесь stdout”hey” переносится в буфер и передает stdin в cat. Поскольку аргументов командной строки не было, cat по умолчанию выбрал stdin для чтения, а что-то уже было в stdin, поэтому команда cat приняла это и напечатала stdout.
Фактически, мы создали файл с именем hey и поместили в него некоторый контент.
Типы каналов в Linux
В Linux есть два типа каналов:
- Безымянный канал, также называемый анонимным.
- Именованные каналы
Каналы без названия
Без названия каналы, как следует из названия, не имеют имени. Они создаются на лету вашей оболочкой Unix всякий раз, когда вы используете символ |.
Когда люди говорят о каналах в Linux, они обычно говорят об этом. Они полезны, потому что вам, как конечному пользователю, не нужно ничего отслеживать. Ваша оболочка справится со всем этим.
Именованные каналы
Этот немного отличается. Именованные каналы действительно присутствуют в файловой системе. Они существуют как обычный файл. Вы можете создать именованный файл, используя следующую команду:
Это создаст файл с именем «pipe». Выполните следующую команду:
Обратите внимание на «p» в начале, это означает, что файл является каналом. Теперь воспользуемся этим каналом.
Как мы говорили ранее, конвейер пересылает вывод одной команды на вход другой. Это как курьерская служба, вы даете посылку доставить с одного адреса, а они доставляют по другому. Итак, первый шаг – предоставить пакет, то есть предоставить каналу что-то.
Вы заметите, что echo еще не вернули нам терминал. Откройте новый терминал и попробуйте прочитать из этого файла.
Обе эти команды завершили свое выполнение одновременно.
Это одно из фундаментальных различий между обычным файлом и именованным каналом. В канал ничего не записывается, пока какой-либо другой процесс не прочитает его.
Зачем использовать именованные каналы? Вот список того, почему вы хотите использовать именованные каналы
Именованные каналы не занимают память на диске
Если вы выполните a du -s pipe, вы увидите, что он не занимает места. Это потому, что именованные каналы похожи на конечные точки для чтения и записи из буфера памяти и в него. Все, что записывается в именованный канал, фактически сохраняется во временном буфере памяти, который сбрасывается, когда операция чтения выполняется из другого процесса.
Сниженный ввод-вывод
Поскольку запись в именованный канал означает сохранение данных в буфере памяти, операции с большими файлами значительно сокращают дисковый ввод-вывод.
Связь между двумя очень разными процессами
Выходные данные события можно мгновенно и очень эффективно получить из другого процесса с помощью именованных каналов. Поскольку чтение и запись происходят одновременно, время ожидания практически равно нулю.
Понимание каналов более низкого уровня [для опытных пользователей и разработчиков]
В следующем разделе речь идет о каналах на более глубоком уровне с фактическими реализациями. Этот раздел требует от вас базового понимания:
- Как работает программа на C
- Что такое системные вызовы
- Что такое процессы
- Что такое файловые дескрипторы
Мы не будем вдаваться в подробности примеров. Речь идет только о «каналах». Для большинства пользователей Linux этот раздел не нужен.
В конце мы предоставили для компиляции образец файла – Makefile. Имейте в виду, что эти программы предназначены только для иллюстративных целей, поэтому, если вы видите, что ошибки обрабатываются некорректно.
Рассмотрим следующий пример программы:
В строке 16 мы создали безымянный канал, используя функцию pipe(). Первое, что следует заметить, это то, что мы передали массив целых чисел со знаком длиной 2.
Это потому, что канал – это не что иное, как массив из двух целых чисел без знака, представляющих два файловых дескриптора. Один для письма, один для чтения. И оба они указывают на расположение буфера в памяти, которое обычно составляет 1 МБ.
Здесь мы назвали переменную fd. fd [0] – дескриптор входного файла, fd [1] – дескриптор выходного файла. В этой программе один процесс записывает строку в файловый дескриптор fd [1], а другой процесс читает из файлового дескриптора fd [0].
Именованный канал ничем не отличается. С именованным каналом вместо двух файловых дескрипторов вы получаете имя файла, которое можно открыть из любого процесса и работать с ним, как с любым другим файлом, учитывая при этом характеристики канала.
Вот пример программы, которая делает то же самое, что и предыдущая программа, но вместо анонимного канала создает именованный канал.
Здесь мы использовали системный вызов mknod для создания именованного канала. Как вы можете видеть, хотя мы удалили канал по завершении, вы вполне можете оставить его и легко использовать для связи между различными программами, просто открыв и записав в файл с именем «npipe» в моем случае.
Вам также не придется создавать два разных канала для двусторонней связи, как в случае с анонимными каналами.
Вот образец Makefile, как и было обещано. Поместите его в тот же каталог, что и предыдущие программы, с именами «pipe.c» и «fifo.c» соответственно.
Вот так. Это действительно все, что есть в каналах Unix.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Источник
Learn Linux, 101: Streams, pipes, and redirects
Getting comfortable with Linux plumbing
By Ian Shields
Updated May 18, 2015 | Published October 14, 2009
Overview
This tutorial grounds you in the basic Linux techniques for redirecting standard IO streams. Learn to:
- Redirect the standard IO streams: standard input, standard output, and standard error
- Pipe output from one command to the input of another
- Send output to both stdout and a file
- Use command output as arguments to another command
This tutorial helps you prepare for Objective 103.4 in Topic 103 of the Linux Server Professional (LPIC-1) exam 101. The objective has a weight of 4.
Input, output, and streams
A Linux shell, such as Bash, receives input and sends output as sequences or streams of characters. Each character is independent of the one before it and the one after it. The characters are not organized into structured records or fixed-size blocks. Streams are accessed using file IO techniques, whether or not the actual stream of characters comes from or goes to a file, a keyboard, a window on a display, or some other IO device. Linux shells use three standard I/O streams, each of which is associated with a well-known file descriptor:
About this series
This series of tutorials helps you learn Linux system administration tasks. You can also use the material in these tutorials to prepare for the Linux Professional Institute’s LPIC-1: Linux Server Professional Certification exams.
See “Learn Linux, 101: A roadmap for LPIC-1” for a description of and link to each tutorial in this series. The roadmap is in progress. This tutorial reflects the Version 5.0 objectives as updated on October 29, 2018. As tutorials are completed, they will be added to the roadmap.
- stdout is the standard output stream, which displays output from commands. It has file descriptor 1.
- stderr is the standard error stream, which displays error output from commands. It has file descriptor 2.
- stdin is the standard input stream, which provides input to commands. It has file descriptor 0.
Input streams provide input to programs, usually from terminal keystrokes. Output streams print text characters, usually to the terminal. The terminal was originally an ASCII typewriter or display terminal, but now, it is more often a text window on a graphical desktop.
If you already studied the tutorial “Learn Linux 101: Text streams and filters,” then some of the material in this tutorial will be familiar to you.
Prerequisites
To get the most from the tutorials in this series, you should have a basic knowledge of Linux and a working Linux system on which you can practice the commands covered in this tutorial. Sometimes different versions of a program format output differently, so your results might not always look exactly like the listings and figures shown here. The examples in this tutorial use Fedora 22, and they should work on any Linux system.
Setting up the examples
In this tutorial, you practice the commands using some of the files created in the tutorial “Learn Linux 101: Text streams and filters.” But if you haven’t read that tutorial, or if you didn’t save the files you worked with, no problem! Start by creating a new subdirectory in your home directory called lpi103-4 and creating the necessary files in it. Do so by opening a text window with your home directory as your current directory. Then copy the contents of Listing 1 and paste it into the window to run the commands that create the lpi103-4 subdirectory and the files that you use. Hint: On most X Windows® systems, pressing the middle mouse button pastes selected text at the cursor position. The selected text can be in the same or another window.
Listing 1. Creating the example files
Your window should look similar to Listing 2, and your current directory should now be the newly created lpi103-4 directory.
Listing 2. Creating the example files – output
Redirecting standard IO
Although the model for standard input and output is a serial stream of characters to and from an ASCII terminal, you might want to prepare input data in a file, or save output or error information in a file. That’s where redirection comes in.
Redirecting output
There are two ways to redirect output to a file:
n>
redirects output from file descriptor n to a file. You must have write authority to the file. If the file does not exist, it is created. If it does exist, the existing contents are usually lost without any warning.
n>>
also redirects output from file descriptor n to a file. Again, you must have write authority to the file. If the file does not exist, it is created. If it does exist, the output is appended to the existing file.
The n in n> or n>> refers to the file descriptor. If it omitted, then standard output (file descriptor 1) is assumed. Listing 3 illustrates using redirection to separate the standard output and standard error from the ls command using files you created earlier in your lpi103-4 directory. It also illustrates appending output to existing files.
Listing 3. Output redirection
I said that output redirection using n> usually overwrites existing files. You can control this with the noclobber option of the set builtin. If it has been set, you can override it using n>| as shown in Listing 4.
Listing 4. Output redirection with noclobber
Sometimes, you might want to redirect both standard output and standard error into a file. This is often done for automated processes or background jobs so that you can review the output later. Use &> or &>> to redirect both standard output and standard error to the same place. Another way of doing this is to redirect file descriptor n and then redirect file descriptor m to the same place using the construct m>&n or m>>&n. The order in which outputs are redirected is important. For example,
command 2>&1 >output.txt
is not the same as
command >output.txt 2>&1
In the first case, stderr is redirected to the current location of stdout and then stdout is redirected to output.txt, but this second redirection affects only stdout, not stderr. In the second case, stderr is redirected to the current location of stdout and that is output.txt. We illustrate these redirections in Listing 5. Notice in the last command that standard output was redirected after standard error, so the standard error output still goes to the terminal window.
Listing 5. Redirecting two streams to one file
At other times, you might want to ignore either standard output or standard error entirely. To do this, redirect the appropriate stream to the empty file, /dev/null. Listing 6 shows how to ignore error output from the ls command and also use the cat command to show you that /dev/null is, indeed, empty.
Listing 6. Ignoring output using /dev/null
Redirecting input
Just as you can redirect the stdout and stderr streams, so too can you redirect stdin from a file, using the tr command to replace the spaces in your text1 file with tabs. In that example, you used the output from the cat command to create standard input for the tr command. Instead of needlessly calling cat , you can now use input redirection to translate the spaces to tabs, as shown in Listing 7.
Listing 7. Input redirection
Shells, including bash, also have the concept of a here-document, which is another form of input redirection. This uses the
You might wonder if you couldn’t have just typed sort -k2 sort-k2 , entered your data, and then pressed Ctrl-d to signal the end of input. The short answer is that you could, but you would not have learned about here-documents. The real answer is that here-documents are more often used in shell scripts. ( A script doesn’t have any other way of signaling which lines of the script should be treated as input.) Because shell scripts make extensive use of tabbing to provide indenting for readability, there is another twist to here-documents. If you use cat commands, which each read from a here-document. Note that you use END as the sentinel for the here-document you are reading from the terminal. If you used the same word for the sentinel within the script, you would end your typing prematurely. So, you use EOF instead. After your script is created, you use the . (dot) command to source it, which means to run it in the current shell context.
Listing 9. Input redirection with a here-document
You learn more about command substitution and scripting in later tutorials of this series. See the series roadmap for a description of and link to each tutorial in the series.
Creating pipelines
In the tutorial “Learn Linux 101: Text streams and filters,” I described text filtering as the process of taking an input stream of text and performing some conversion on the text before sending it to an output stream. Such filtering is most often done by constructing a pipeline of commands where the output from one command is piped or redirected to be used as input to the next. Using pipelines in this way is not restricted to text streams, although that is often where they are used.
Piping stdout to stdin
You use the | (pipe) operator between two commands to direct the stdout of the first to the stdin of the second. You construct longer pipelines by adding more commands and more pipe operators. Any of the commands can have options or arguments. Many commands use a hyphen (-) in place of a filename as an argument to indicate when the input should come from stdin rather than a file. Check the man pages for the command to be sure. Constructing long pipelines of commands that each have limited capability is a common Linux and UNIX® way of accomplishing tasks. In the hypothetical pipeline in Listing 10, command2 and command3 both have parameters, while command3 uses the — parameter to signify input from stdin, along with some other hypothetical parameter.
Listing 10. Piping output through several commands
One thing to note is that pipelines only pipe stdout to stdin. You cannot use 2| to pipe stderr alone, at least, not with the tools you have learned so far. If stderr has been redirected to stdout, then both streams will be piped. Listing 11 illustrates an unlikely ls command with four wildcard arguments that are not in alphabetical order, then uses a pipe sort the combined normal and error output.
Listing 11. Piping two output streams
One point of interest here. If the output of ls is the terminal, then it is formatted in as many columns as will fit across your terminal window. If it is redirected, then there is one output entry per line. This makes it much easier to do something further with each one of a set of files.
One advantage of pipes on Linux and UNIX systems is that, unlike some other popular operating systems, there is no intermediate file involved with a pipe. The stdout of the first command is not written to a file and then read by the second command. In the tutorial “Learn Linux, 101: File and directory management,” you learn how to archive and compress a file in one step using the tar command. If you happen to be working on a UNIX system where the tar command does not support compression using the -z (for gzip) or -j (for bzip2) compression, that’s no problem. You can just use a pipeline like
bunzip2 -c somefile.tar.bz2 | tar -xvf —
Starting pipelines with a file instead of stdout
In the previous pipelines, you started with some command that generated output and then piped that output through each stage of the pipeline. What if you want to start with a file of data that already exists? Many commands take either stdin or a file as input, so those aren’t a problem. A program that takes input from stdin and sends output to stdout, is often called a filter. If you have a filter that requires input from stdin, you might think of using the cat command to copy the file to stdout and then pipe it to your command, which would work. However, you can use input redirection for the first command and then pipe that command’s output through the rest of the pipeline for the more usual solution. Just use the xargs command
You will learn about the first two of these now. You saw an example of command substitution in Listing 9 when you created a captive tab character. Command substitution is used at the command line, but more frequently in scripting; you learn more about it and scripting in later tutorials of this series. See the series roadmap for a description of and link to each tutorial in the series.
Using the xargs command
The xargs command reads standard input and then builds and executes commands with the input as parameters. If no command is given, then the echo command is used. Listing 12 is a basic example using your text1 file, which contains three lines, each having two words.
Listing 12. Using xargs
So why is there only one line of output from xargs ? By default, xargs breaks the input at blanks, and each resulting token becomes a parameter. However, when xargs builds the command, it passes as many parameters at once as it possibly can. You may override this behavior with the -n , or —max-args , parameter. Listing 13 illustrates the use of both forms and adds an explicit invocation of echo to your use of xargs .
Listing 13. Using xargs and echo
If the input contains blanks that are protected by single or double quotes, or by backslash escaping, then xargs will not break the input at such points. Listing 14 illustrates this point.
Listing 14. Using xargs with quoting
So far, all the arguments have been added to the end of the command. If you have a requirement to use them as arguments with other arguments following, then you use the -I option to specify a replacement string. Wherever the replacement string occurs in the command you ask xargs to execute, it will be replaced by an argument. When you do this, only one argument is passed to each command. However, the argument is created from a whole line of input, not just a single token. You can also use the -L option of xargs to have it treat lines as arguments rather than the default of individual blank-delimited tokens. Using the -I option implies -L 1 -L1 . Listing 15 shows examples of the use of both -I and -L .
Listing 15. Using xargs with lines of input
Although our examples have used simple text files for illustration, you will seldom want to use xargs with input like this. Usually, you will be dealing with a large list of files generated from a command such as ls , find , or grep . Listing 16 shows one way you might pass a directory listing through xargs to a command such as grep .
Listing 16. Using xargs with lists of files
What happens in the last example if one or more of the file names contains a space? If you just used the command as in Listing 16, then you would get an error. In the real world, your list of files might come from some source such as a custom script or command, rather than ls , or you might wish to pass it through some other pipeline stages to further filter it, so we’ll ignore the fact that you could just use grep “1” `grep”1″` instead of this construct.
For the ls command, you could use the —quoting-style option to force the problem file names to be quoted or escaped. A better solution, when available, is the -0 option of xargs , so that the null character (\0) is used to delimit input arguments. Although ls does not have an option to provide null-terminated file names as output, many commands do.
Listing 17, first copies text1 to “text 1” and then shows some ways of using a list of file names containing blanks with xargs . These are for illustration of the concepts, as xargs can be tricky to master. In particular, the final example of translating new line characters to nulls would not work if some file names already contained new line characters. In the next part of this tutorial, you see a more robust solution using the find command to generate suitable null-delimited output.
Listing 17. Using xargs with blanks in file names
The xargs command does not build arbitrarily long commands. Until Linux kernel 2.26.3, the maximum size of a command was limited. A command such as rm somepath/* , for a directory containing a lot of files with long names, might fail with a message indicating that the argument list was too long. In the older Linux systems, or on UNIX systems that might still have the limitation, it is useful to know how to use xargs so that you can handle such situations
You can use the —show-limits option to display the default limits of xargs , and the -s option to limit the size of output commands to a specific maximum number of characters. See the man pages for other options that are not covered here.
Using the find command with the -exec option or with xargs
In the tutorial “Learn Linux, 101: File and directory management,” you learn how to use the find command to find files by name, modification time, size, or other characteristics. Once you find such a set of files, you often want to do something with them: remove them, copy them to another location, rename them, or some other operation. Now, you look at the -exec option of find , which has similar functionality to using find and piping the output to xargs .
Listing 18. Using find and -exec
Compared to what you already learned about using xargs , there are several differences:
- You must include the <> to mark where the filename goes in the command. It is not automatically added at the end.
- You must terminate the command with a semi-colon and it must be escaped; \;, ‘;’, or “;” will do.
- The command is executed once for each input file.
Try running find text[12] |xargs cat text3 findtext[12]|xargscattext3 to see the differences for yourself.
Now let’s return to the case of the blank in the file name. In Listing 19, you try using find with -exec rather than ls with xargs .
Listing 19. Using find and -exec with blanks in file names
So far, so good. But isn’t there something missing? Which files contained the lines found by grep ? The file name is missing because find called grep once for each file, and grep is smart enough to know that if you only give it one file name, then you don’t need it to tell you what file it was.
You could use xargs instead, but you already saw problems with blanks in file names. I also alluded to the fact that find could produce a list of file names with null delimiters, and that is what the -print0 option does. Modern versions of find may be delimited with a + sign instead of a semi-colon, and this causes find to pass as many names as possible in one invocation of a command, similar to the way xargs works. Needless to say, you can only use <> once in this case, and it must be the last parameter of the command. Listing 20 shows both methods.
Listing 20. Using find and xargs with blanks in file names
Generally, either method works, and the choice is often a matter of personal style. Remember that piping things with unprotected blanks or white space can cause problems, so use the -print0 option with find if you are piping the output to xargs , and use the -0 option to tell xargs to expect null-delimited input. Other commands, including tar , also support null-delimited input using the -0 option, so you should always use it for commands that support it unless you are certain that your input list will not be a problem.
One final comment on operating on a list of files. It’s always a good idea to thoroughly test your list and also to test your command very carefully before committing to a bulk operation such as removing or renaming files. Having a good backup can be invaluable too.
Splitting output
This section wraps up with a brief discussion of one more command. Sometimes you might want to see output on your screen while saving a copy for later. While you could do this by redirecting the command output to a file in one window and then using tail -fn1 tail-fn1 to follow the output in another screen, using the tee command is easier.
You use tee with a pipeline. The arguments are a file (or multiple files) for standard output. The -a option appends rather than overwriting files. As you saw earlier in our discussion of pipelines, you need to redirect stderr to stdout before piping to tee if you want to save both. Listing 21 shows tee being used to save output in two files, f1 and f2.
Источник