Потоки, программные каналы и перенаправления. Перенаправление _только_ stderr в pipe

Потоки, программные каналы и перенаправления. Перенаправление _только_ stderr в pipe

16.03.2019

4 ответов

Определение:

Контрольные точки данных позволяют когда значение, хранящееся на указанные изменения в памяти.

Как установить точку останова памяти

    В меню "Отладка" выберите "Новая точка останова" и нажмите "Новая точка останова данных"

    в окне "Точки останова" выберите "Новое раскрывающееся меню" и выберите "Новая точка останова данных".

    Появится диалоговое окно New Breakpoint.

    В поле "Адрес" введите адрес памяти или выражение, которое оценивает адрес памяти. Например, & foo для разрыва при изменении содержимого переменной foo.

    В поле "Количество байтов" введите количество байтов, которое вы хотите отследить отладчика. Например, если вы введете 4, отладчик будет следить за четырьмя байтами, начинающимися с & foo и break, если какой-либо из этих байтов изменит значение.

    Нажмите "ОК".

    До сих пор мы получили отличное определение и кучу великих теоретических объяснений.

    Приведем конкретный пример!

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

    К счастью, у нас есть возможность включить диспетчер памяти отладки, который проверяет такие вещи. Я включил его, и он сразу же начал сообщать о нарушении защиты блока памяти, что означает, что что-то вышло за рамки. Проблема в том, что этот отчет появляется только после освобождения памяти - по существу, говоря: "эй, что-то было сломано. Надеюсь, ты сможешь понять, что!"

    К сожалению, этот конкретный кусок памяти, в момент освобождения, полностью неотличим от буквально тысяч других кусков памяти. К счастью, наши метки отладки основывают каждое выделение с последовательным идентификатором, а поврежденная память имеет согласованный идентификатор (# 9667, если вам интересно). Одна быстрая точка останова в менеджере памяти позже, и я смог найти, где это память была выделена. Который, как выяснилось, тоже не помог.

    Но в этот момент у меня было несколько важных компонентов:

    • Я знал адрес блока памяти
    • Я знал предполагаемую длину этой памяти
    • Я знал, что в какой-то момент в будущем конкретный байт, предшествующий предполагаемой длине этой памяти, будет перезаписан.

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

    Оператор for-in предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной. Синтаксис следующий:

    for переменная in список_значений do команды done

    Рассмотрим небольшой пример:

    #!/bin/bash for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно do echo "Console number is $i " >> / dev/ pts/ $i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i" done #цикл окончен exit 0

    После выполнения примера в первых 5 виртуальных консолях (терминалах) появится строка с её номером. В переменную $i поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной.

    Циклы. Цикл while

    Цикл while сложнее цикла for-in и используется для повторения команд, пока какое-то выражение истинно(код возврата = 0). Синтаксис оператора следующий:

    while выражение или команда возвращающая код возврата do команды done

    Пример работы цикла рассмотрим на следующем примере:

    #!/bin/bash again =yes #присваиваем значение "yes" переменной again while [ "$again " = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes" do echo "Please enter a name:" read name echo "The name you entered is $name " echo "Do you wish to continue?" read again done echo "Bye-Bye"

    А теперь результат работы скрипта:

    Ite@ite-desktop:~$ ./bash2_primer1.sh Please enter a name: ite The name you entered is ite Do you wish to continue? yes Please enter a name: mihail The name you entered is mihail Do you wish to continue? no Bye-Bye

    Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do и done можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.

    Теперь об условии истинности. После while, как и в условном операторе if-then-else можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор »[» аналог команды test, которая проверяет истинность условия, которое ей передали.

    Рассмотрим еще один пример, я взял его из книги Advanced Bash scripting. Уж очень он мне понравился Улыбка, но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO. Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.

    Вот пример:

    #!/bin/bash echo "Введите числитель: " read dividend echo "Введите знаменатель: " read divisor dnd =$dividend #мы будем изменять переменные dividend и divisor, #сохраним их знания в других переменных, т.к. они нам #понадобятся dvs =$divisor remainder =1 until [ "$remainder " -eq 0 ] do let "remainder = dividend % divisor" dividend =$divisor divisor =$remainder done echo "НОД чисел $dnd и $dvs = $dividend "

    Результат выполнения скрипта:

    Ite@ite-desktop:~$ ./bash2_primer3.sh Введите числитель: 100 Введите знаменатель: 90 НОД чисел 100 и 90 = 10

    Математические операции

    Команда let.

    Команда let производит арифметические операции над числами и переменными.

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

    #!/bin/bash echo "Введите a: " read a echo "Введите b: " read b let "c = a + b" #сложение echo "a+b= $c " let "c = a / b" #деление echo "a/b= $c " let "c <<= 2" #сдвигает c на 2 разряда влево echo "c после сдвига на 2 разряда: $c " let "c = a % b" # находит остаток от деления a на b echo "$a / $b . остаток: $c "

    Результат выполнения:

    Ite@ite-desktop:~$ ./bash2_primer2.sh Введите a: 123 Введите b: 12 a+b= 135 a/b= 10 c после сдвига на 2 разряда: 40 123 / 12. остаток: 3

    Ну вот, как видите ничего сложного, список математических операций стандартный:

    Сложение
    - - вычитание
    * - умножение
    / - деление
    ** - возведение в степень
    % - модуль(деление по модулю), остаток от деления

    let позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных.

    Например: a = a+b эквивалентно a +=b и т.д.

    Работа с внешними программами при написании shell-скриптов

    Для начала немного полезной теории.

    Перенаправление потоков

    В bash (как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin), 1 (stdout), 2 (stderr).

    stdout - Стандартный вывод. Сюда попадает все что выводят программы
    stdin - Стандартный ввод. Это все что набирает юзер в консоли
    stderr - Стандартный вывод ошибок.

    Для операций с этими дескрипторами, существуют специальные символы: > (перенаправление вывода), < (перенаправление ввода). Оперировать ими не сложно. Например:

    cat / dev/ random > / dev/ null

    перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция ) или

    ls -la > listing

    записать в файл listing содержание текущего каталога (уже полезней)

    Если есть необходимость дописывать в файл (при использовании »>» он заменяется), необходимо вместо »>» использовать »>>»

    sudo < my_password

    после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.

    Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:

    ./ program_with_error 2 > error_file

    цифра 2 перед »>» означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).

    Если необходимо заставить stderr писать в stdout, то это можно можно след. образом:

    ./ program_with_error 2 >& 1

    символ »&» означает указатель на дескриптор 1(stdout)

    (По умолчанию stderr пишет на ту консоль, в которой работает пользователь (вернее пишет на дисплей)).

    2. Конвейеры

    Конвейер - очень мощный инструмент для работы с консолью Bash. Синтаксис простой:

    команда1 | команда 2 - означает, что вывод команды 1 передастся на ввод команде 2

    Конвейеры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:

    ls -la | grep "hash" | sort > sortilg_list

    вывод команды ls -la передается команде grep, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort, которая пишет результат в файл sorting_list. Все довольно понятно и просто.

    Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout одной команды и передача на stdin другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постараюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.

    1. Передача вывода в переменную

    Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в `` кавычки, например

    A = ` echo "qwerty" ` echo $a

    Результат работы: qwerty

    Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:

    LIST =` find / svn/ -type d 2>/ dev/ null| awk "{FS="/"} {print $4}" | sort | uniq | tr "\n" " " ` for ONE_OF_LIST in $LIST do svnadmin hotcopy / svn/ $ONE_OF_LIST / svn/ temp4backup/ $ONE_OF_LIST done

    Здесь мы используем цикл for-do-done для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=`find /svn/ -type d 2>/dev/null| awk "{FS=»/»} {print $4}"| sort|uniq | tr "\n" " "` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.

    Как видно, все не сложно, достаточно понять принцип и написать пару своих скриптов. В заключении статьи хочу пожелать удачи в изучении BASH и Linux в целом. Критика, как водится приветствуется. Следующая статья возможно будет посвящена использованию таких программ как sed, awk.

    Изучаем Linux, 101

    Потоки, программные каналы и перенаправления

    Изучение основ работы с конвейерами Linux

    Серия контента:

    Краткий обзор

    Из этой статьи вы узнаете об основных приемах перенаправления стандартных потоков ввода/вывода в Linux. Вы научитесь:

    • Перенаправлять стандартные потоки ввода/вывода: стандартный поток ввода, стандартный поток вывода и стандартный поток ошибок.
    • Направлять вывод одной команды на вход другой команды.
    • Отправлять вывод одновременно на стандартное устройство вывода (stdout) и в файл.
    • Использовать вывод команды в качестве аргументов другой команды.

    Эта статья поможет вам подготовиться к сдаче экзамена LPI 101 на администратора начального уровня (LPIC-1) и содержит материалы цели 103.4 темы 103. Цель имеет вес 4.

    Об этой серии

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

    Чтобы посмотреть описания статей этой серии и получить ссылки на них, обратитесь к нашему . Этот перечень постоянно дополняется новыми статьями по мере их готовности и содержит самые последние (по состоянию на апрель 2009 года) цели экзаменов сертификации LPIC-1. Если какая-либо статья отсутствует в перечне, можно найти ее более раннюю версию, соответствующую предыдущим целям LPIC-1 (до апреля 2009 года), обратившись к нашим .

    Необходимые условия

    Чтобы извлечь наибольшую пользу из наших статей, необходимо обладать базовыми знаниями о Linux и иметь работоспособный компьютер с Linux, на котором можно будет выполнять все встречающиеся команды. Иногда различные версии программ выводят результаты по-разному, поэтому содержимое листингов и рисунков может отличаться от того, что вы увидите на вашем компьютере.

    Подготовка к выполнению примеров

    Как связаться с Яном

    Ян – один из наших наиболее популярных и плодовитых авторов. Ознакомьтесь со (EN), опубликованными на сайте developerWorks. Вы можете найти контактные данные в и связаться с ним, а также с другими авторами и участниками ресурса My developerWorks.

    Для выполнения примеров в этой статье мы будем использовать некоторые файлы, созданные ранее в статье " ". Если вы не читали эту статью или не сохранили эти файлы, не расстраивайтесь! Давайте начнем с создания новой директории lpi103-4 и всех необходимых файлов. Для этого откройте текстовое окно и перейдите в вашу домашнюю директорию. Скопируйте содержимое листинга 1 в текстовое окно; в результате выполнения команд в вашей домашней директории будет создана поддиректория lpi103-4 и все необходимые файлы в ней, которые мы и будем использовать в наших примерах.

    Листинг 1. Создание файлов, необходимых для примеров этой статьи
    mkdir -p lpi103-4 && cd lpi103-4 && { echo -e "1 apple\n2 pear\n3 banana" > text1 echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 echo "This is a sentence. " !#:* !#:1->text3 split -l 2 text1 split -b 17 text2 y; }

    Ваше окно должно выглядеть так, как показано в листинге 2, а вашей текущей рабочей директорией должна стать вновь созданная директория lpi103-4.

    Листинг 2. Результаты создания необходимых файлов
    $ mkdir -p lpi103-4 && cd lpi103-4 && { > echo -e "1 apple\n2 pear\n3 banana" > text1 > echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 > echo "This is a sentence. " !#:* !#:1->text3echo "This is a sentence. " "This is a sentence. " "This is a sentence.">text3 > split -l 2 text1 > split -b 17 text2 y; } $

    Перенаправление стандартного ввода/вывода

    Командный интерпретатор Linux, такой как Bash, получает входные данные и направляет выходные данные в виде последовательностей или потоков символов. Любой символ не зависит ни от предыдущих, ни от последующих символов. Символы не упорядочены в виде структурированных записей или блоков с фиксированным размером. Доступ к потокам осуществляется с помощью механизмов ввода/вывода независимо от того, откуда поступают и куда передаются потоки символов (файл, клавиатура, окно, экран или другое устройство ввода/вывода). Командные интерпретаторы Linux используют три стандартных потока ввода/вывода, каждому из которых назначен определенный файловый дескриптор.

    1. stdout стандартный поток вывода , отображает вывод команд и имеет дескриптор 1.
    2. stderr стандартный поток ошибок , отображает ошибки команд и имеет дескриптор 2.
    3. stdin стандартный поток ввода , передает входные данные командам и имеет дескриптор 0.

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

    Если вы уже прочитали руководство " ", то часть материала из этой статьи окажется вам знакомой.

    Перенаправление вывода

    Существует два способа перенаправления вывода в файл:

    n > перенаправляет вывод из файлового дескриптора n в файл. Вы должны иметь права на запись в файл. Если файла не существует, то он будет создан. Если файл существует, то все его содержимое, как правило, уничтожается без каких-либо предупреждений. n >> также перенаправляет вывод из файлового дескриптора n в файл. Вы также должны иметь права на запись в файл. Если файла не существует, то он будет создан. Если файл существует, то вывод добавляется к его содержимому.

    Символ n в операторах n> или n>> является файловым дескриптором . Если он не указан, то предполагается, что используется стандартное устройство вывода. В листинге 3 продемонстрирована операция перенаправления для разделения стандартного потока вывода и стандартного потока ошибок команды ls с использованием файлов, которые были созданы ранее в директории lpi103-4. Также продемонстрировано добавление вывода команды в существующие файлы.

    Листинг 3. Перенаправление вывода
    $ ls x* z* ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >stdout.txt 2>stderr.txt $ ls w* y* ls: cannot access w*: No such file or directory yaa yab $ ls w* y* >>stdout.txt 2>>stderr.txt $ cat stdout.txt xaa xab yaa yab $ cat stderr.txt ls: cannot access z*: No such file or directory ls: cannot access w*: No such file or directory

    Мы уже говорили, что перенаправление вывода с помощью оператора n> обычно приводит к перезаписи существующих файлов. Вы можете управлять этим свойством при помощи опции noclobber встроенной команды set . Если эта опция определена, то вы можете переопределить ее с помощью оператора n>|, как показано в листинге 4.

    Листинг 4. Перенаправление вывода с помощью опции noclobber
    $ set -o noclobber $ ls x* z* >stdout.txt 2>stderr.txt -bash: stdout.txt: cannot overwrite existing file $ ls x* z* >|stdout.txt 2>|stderr.txt $ cat stdout.txt xaa xab $ cat stderr.txt ls: cannot access z*: No such file or directory $ set +o noclobber #restore original noclobber setting

    Иногда может потребоваться перенаправить в файл как стандартный вывод, так и стандартный поток ошибок. Часто это используется в автоматизированных процессах или фоновых заданиях для того, чтобы впоследствии можно было просмотреть результаты работы. Чтобы перенаправить стандартный вывод и стандартный поток ошибок в одно и то же место, используйте оператор &> или &>>. Альтернативный вариант – перенаправить файловый дескриптор n и затем файловый дескриптор m в одно и то же место с помощью конструкции m>&n или m>>&n. В этом случае важен порядок перенаправления потоков. Например, команда
    command 2>&1 >output.txt
    это не то же самое, что команда
    command >output.txt 2>&1
    В первом случае поток ошибок stderr перенаправляется в текущее месторасположение потока stdout, а затем поток stdout перенаправляется в файл output.txt; однако второе перенаправление затрагивает только stdout, но не stderr. Во втором случае поток stderr перенаправляется в текущее месторасположение потока stdout, то есть, в файл output.txt. Эти перенаправления проиллюстрированы в листинге 5. Обратите внимание на последнюю команду, в которой стандартный вывод был перенаправлен после стандартного потока ошибок, и, как следствие, поток ошибок продолжает выводиться в окно терминала.

    Листинг 5. Перенаправление двух потоков в один файл
    $ ls x* z* &>output.txt $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >output.txt 2>&1 $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt ls: cannot access z*: No such file or directory $ cat output.txt xaa xab

    В других ситуациях вам может потребоваться полностью проигнорировать стандартный вывод или стандартный поток ошибок. Для этого следует перенаправить соответствующий поток в пустой файл /dev/null. В листинге 6 показано, как проигнорировать поток ошибок команды ls и как с помощью команды cat убедиться в том, что файл /dev/null на самом деле пуст.

    Листинг 6. Игнорирование стандартного потока ошибок посредством использования /dev/null
    $ ls x* z* 2>/dev/null xaa xab $ cat /dev/null

    Перенаправление ввода

    Так же, как мы можем перенаправить потоки stdout и stderr, мы можем перенаправить поток stdin из файла с помощью оператора <. Если вы прочли руководство " ", то должны помнить, что в разделе была использована команда tr для замены пробелов в файле text1 на символы табуляции. В том примере мы использовали вывод команды cat чтобы создать стандартный поток ввода для команды tr . Теперь для преобразования пробелов в символы табуляции вместо бесполезного вызова команды cat мы можем использовать перенаправление ввода, как показано в листинге 7.

    Листинг 7. Перенаправление ввода
    $ tr " " "\t"

    В командных интерпретаторах, в том числе и в bash, реализована концепция here-document , которая является одним из способов перенаправления ввода. В ней используется конструкция << и какое-либо слово, например END, являющееся маркером, или сигнальной меткой, означающей конец ввода. Эта концепция продемонстрирована в листинге 8.

    Листинг 8. Перенаправление ввода с использованием концепции here-document
    $ sort -k2 < 1 apple > 2 pear > 3 banana > END 1 apple 3 banana 2 pear

    Но почему нельзя просто набрать команду sort -k2 , ввести данные и нажать комбинацию Ctrl-d , означающую конец ввода? Разумеется, вы могли бы выполнить эту команду, но тогда вы не узнали бы о концепции here-document, которая очень часто используется в сценариях командной оболочки (в которых не существует другой возможности указать, какие именно строки должны восприниматься в качестве ввода). Поскольку для выравнивания текста и обеспечения удобства чтения в сценариях широко используются символы табуляции, существует другой прием использования концепции here-document. При использовании оператора <<- вместо оператора << начальные символы табуляции удаляются.

    В листинге 9 мы использовали подстановку команд для создания символа табуляции, а затем создали небольшой сценарий командной оболочки, содержащий две команды cat , каждая из которых считывает данные из блока here-document. Заметьте, что мы использовали слово END в качестве сигнальной метки блока here-document, который мы считываем с терминала. Если бы мы использовали это же слово в нашем сценарии, то наш ввод закончился бы преждевременно. Поэтому вместо слова END мы используем в сценарии слово EOF. После того, как наш сценарий создан, мы используем команду. (точка) чтобы запустить его в контексте текущего командного интерпретатора.

    Листинг 9. Перенаправление ввода с использованием концепции here-document
    $ ht=$(echo -en "\t") $ cat<ex-here.sh > cat <<-EOF > apple > EOF > ${ht}cat <<-EOF > ${ht}pear > ${ht}EOF > END $ cat ex-here.sh cat <<-EOF apple EOF cat <<-EOF pear EOF $ . ex-here.sh apple pear

    В следующих статях этой серии вы узнаете больше о подстановке команд и сценариях. Ссылки на все статьи этой серии вы можете найти в .

    Создание конвейеров

    Использование команды xargs

    Команда xargs считывает данные со стандартного устройства ввода, а затем строит и выполняет команды, параметрами которых являются полученные входные данные. Если не указана никакая команда, то используется команда echo . В листинге 12 приведен простой пример использования нашего файла text1, содержащего три строки по два слова в каждой.

    Листинг 12. Использование команды xargs
    $ cat text1 1 apple 2 pear 3 banana $ xargs

    Почему же тогда вывод xargs содержит только одну строку? По умолчанию xargs разбивает входные данные, если встречает символы-разделители, и каждый полученный фрагмент становится отдельным параметром. Однако когда xargs строит команду, ей за один раз передается максимально возможное количество параметров. Это поведение можно изменить с помощью параметра –n или --max-args . В листинге 13 приведен пример использования обоих вариантов; также был выполнен явный вызов команды echo для использования с xargs .

    Листинг 13. Использование команд xargs и echo
    $ xargs" args > 1 apple 2 pear 3 banana $ xargs --max-args 3 " args > 1 apple 2 args > pear 3 banana $ xargs -n 1 " args > 1 args > apple args > 2 args > pear args > 3 args > banana

    Если входные данные содержат пробелы, но при этом они заключены в одиночные или двойные кавычки (либо пробелы представлены в виде escape-последовательностей с использованием обратной косой черты), то xargs не будет разбивать их на отдельные части. Это показано в листинге 14.

    Листинг 14. Использование команды xargs и кавычек
    $ echo ""4 plum"" | cat text1 - 1 apple 2 pear 3 banana "4 plum" $ echo ""4 plum"" | cat text1 - | xargs -n 1 1 apple 2 pear 3 banana 4 plum

    До сих пор все аргументы добавлялись в конец команды. Если вам необходимо, чтобы после них были добавлены другие дополнительные аргументы, то воспользуйтесь опцией -I для указания строки замещения. В том месте вызываемой через xargs команды, в котором используется строка замещения, вместо нее будет подставлен аргумент. При использовании такого подхода каждой команде передается только один аргумент. Однако аргумент будет создан из целой входной строки, а не из отдельного ее фрагмента. Также вы можете использовать опцию -L команды xargs , в результате чего в качестве аргумента будет использоваться вся строка целиком, а не отдельные ее фрагменты, разделенные пробелами. Использование опции -I неявно вызывает использование опции -L 1 . В листинге 15 приведены примеры использования опций -I и –L .

    Листинг 15. Использование команды xargs и строк ввода
    $ xargs -I XYZ echo "START XYZ REPEAT XYZ END" " <9 plum> <3 banana><3 banana> <10 apple><10 apple> $ cat text1 text2 | xargs -L2 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple

    Хотя в наших примерах используются простые текстовые файлы, вы не будете часто использовать команду xargs для таких случаев. Как правило, вы будете иметь дело с большим списком файлов, полученных в результате выполнения таких команд, как ls , find или grep . В листинге 16 показан один из способов передачи через xargs списка содержимого директории такой команде, как, например, grep .

    Листинг 16. Использование команды xargs и списка файлов
    $ ls |xargs grep "1" text1:1 apple text2:10 apple xaa:1 apple yaa:1

    Что произойдет в последнем примере, если одно или несколько имен файлов будут содержать пробелы? Если вы попытаетесь использовать команду так, как это было сделано в листинге 16, то вы получите ошибку. В реальной ситуации список файлов может быть получен не от команды ls , а, например, в результате выполнения пользовательского сценария или команды; а может быть, вы захотите обработать его на других этапах конвейера с целью дополнительной фильтрации. Поэтому мы не берем во внимание тот факт, что вы могли бы просто использовать команду grep "1" * вместо существующей логической структуры.

    В случае с командой ls вы могли бы использовать опцию --quoting-style для того, чтобы имена файлов, содержащие пробелы, были заключены в скобки (или представлены в виде escape-последовательностей). Лучшим решением (когда это возможно) является использование опции -0 команды xargs , в результате чего для разделения входных аргументов используются пустые символы (\0). Хотя команда ls не имеет опции, позволяющей использовать в качестве вывода имена файлов с завершающим нулем, многие команды умеют делать это.

    В листинге 17 мы сначала скопируем файл text1 в "text 1", а затем приведем несколько примеров использования списка имен файлов, содержащих пробелы, с командой xargs . Эти примеры позволяют понять саму идею, поскольку полностью освоить работу с xargs может оказаться не так просто. В частности, последний пример преобразования символов новой строки в пустые символы не сработал бы в том случае, если некоторые имена файлов уже содержали бы символы новой строки. В следующем разделе этой статьи мы рассмотрим более надежное решение с применением команды find для генерации подходящего вывода, в котором в качестве разделителей используются пустые символы.

    Листинг 17. Использование команды xargs и файлов, содержащих пробелы в именах
    $ cp text1 "text 1" $ ls *1 |xargs grep "1" # error text1:1 apple grep: text: No such file or directory grep: 1: No such file or directory $ ls --quoting-style escape *1 text1 text\ 1 $ ls --quoting-style shell *1 text1 "text 1" $ ls --quoting-style shell *1 |xargs grep "1" text1:1 apple text 1:1 apple $ # Illustrate -0 option of xargs $ ls *1 | tr "\n" "\0" |xargs -0 grep "1" text1:1 apple text 1:1 apple

    Команда xargs не может строить сколь угодно длинные команды. Так, в Linux до версии ядра 2.26.3 максимальная длина команды была ограничена. Если вы попытаетесь выполнить такую команду, как, например, rm somepath/* , а директория содержит множество файлов с длинными именами, то выполнение может завершиться ошибкой, сообщающей, что список аргументов слишком длинный. Если вы работаете с более старыми версиями Linux или UNIX, в которых могут присутствовать такие ограничения, то будет полезно узнать, как можно использовать xargs таким образом, чтобы обойти их.

    Вы можете использовать опцию --show-limits для просмотра ограничений, установленных по умолчанию для команды xargs , и опцию -s – для задания максимальной длины выводимых команд. Об остальных опциях вы можете узнать из man-страниц.

    Использование команды find с опцией -exec или совместно с командой xargs

    Из руководства " " вы узнали о том, как использовать команду find для поиска файлов на основе их имен, времени модификации, размера и прочих характеристик. Обычно над найденными файлами необходимо выполнять определенные действия – удалять, копировать, переименовывать их и так далее. Сейчас мы рассмотрим опцию -exec команды find , работа которой похожа на работу команды find с последующей передачей вывода команде xargs .

    Листинг 18. Использование команды find с опцией -exec
    $ find text -exec cat text3 {} \; This is a sentence. This is a sentence. This is a sentence. 1 apple 2 pear 3 banana This is a sentence. This is a sentence. This is a sentence. 9 plum 3 banana 10 apple

    Сопоставив результаты листинга 18 с тем, что вам уже известно о xargs , можно обнаружить несколько различий.

    1. Вы должны использовать в команде символы {} для указания места подстановки, в которое будет подставлено имя файла. Эти символы не добавляются автоматически в конце команды.
    2. Вы должны завершить команду точкой с запятой, которая должна быть представлена в виде escape-последовательности (\;, ";" или ";").
    3. Команда выполняется один раз для каждого входного файла.

    Попробуйте самостоятельно выполнить команду find text |xargs cat text3 , чтобы увидеть различия.

    Теперь давайте вернемся к случаю, когда имя файла содержит пробелы. В листинге 19 мы попытались использовать команду find с опцией -exec вместо команд ls и xargs .

    Листинг 19. Использование команды find с опцией -exec и файлов, содержащих пробелы в именах
    $ find . -name "*1" -exec grep "1" {} \; 1 apple 1 apple

    Пока все хорошо. Однако не кажется ли вам, что чего-то здесь не хватает? В каких файлах присутствовали строки, найденные при помощи grep ? Здесь не хватает имен файлов, поскольку find вызывает grep один раз для каждого файла, а grep , будучи умной командой, знает о том, что если ей было передано имя лишь одного файла, то нет необходимости сообщать вам о том, что это был за файл.

    В этой ситуации мы могли бы воспользоваться командой xargs , однако мы уже знаем о проблеме с файлами, имена которых содержат пробелы. Также мы упоминали тот факт, что команда find может генерировать список имен с пустыми разделителями, благодаря опции -print0 . Современные версии команды find могут разделяться не точкой с запятой, а знаком +, благодаря чему, за один вызов команды find можно передать максимально возможное число имен, так же, как и в случае использования xargs . Излишне говорить о том, что в этом случае вы можете использовать конструкцию {} только один раз, и что она должна являться последним параметром команды. В листинге 20 продемонстрированы оба этих метода.

    Листинг 20. Использование команд find , xargs и файлов, содержащих пробелы в именах
    $ find . -name "*1" -print0 |xargs -0 grep "1" ./text 1:1 apple ./text1:1 apple $ find . -name "*1" -exec grep "1" {} + ./text 1:1 apple ./text1:1 apple

    Оба этих метода являются рабочими и выбор какого-то одного из них часто обусловлен лишь личными предпочтениями пользователя. Помните о том, что передавая по конвейеру объекты с необработанными символами-разделителями и пробелами, вы можете столкнуться с проблемами; поэтому если вы передаете вывод команде xargs , то используйте опцию -print0 команды find , а также опцию -0 команды xargs , которая сообщает, что во входных данных используются пустые разделители. Другие команды, включая tar , также поддерживают опцию -0 и работу с входными данными, содержащими пустые разделители, поэтому всегда следует использовать эту опцию для тех команд, которые ее поддерживают, если только вы уверены на все 100%, что входной список не создаст вам проблем.

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

    Разделение вывода

    В завершение этой статьи мы кратко рассмотрим еще одну команду. Иногда может возникнуть необходимость просматривать вывод на экране и одновременно сохранять его в файл. Для этого вы могли бы перенаправить вывод команды в файл в одном окне, а затем с помощью tail -fn1 отслеживать вывод в другом окне, однако проще всего использовать команду tee .

    Команда tee используется в конвейере, а ее аргументом является имя файла (или имена нескольких файлов), в который будет передаваться стандартный вывод. Опция -a позволяет не замещать старое содержимое файла новым содержимым, а добавлять данные в конец файла. Как уже говорилось при рассмотрении конвейеризации, если вы хотите сохранить как стандартный вывод, так и поток ошибок, то необходимо перенаправлять поток stderr в поток stdout прежде, чем передавать данные на вход команде tee . В листинге 21 приведен пример использования команды tee для сохранения вывода в два файла, f1 и f2.

    Листинг 21. Разделение потока stdout с помощью команды tee
    $ ls text|tee f1 f2 text1 text2 text3 $ cat f1 text1 text2 text3 $ cat f2 text1 text2 text3

© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows