ruby -e для упрощения работы в консоли

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


    TL;DR


    Запоминаем две конструкции:


    1. ... | ruby -lne <CODE>
    2. ruby -lne <CODE> file1, file2, ...

    В первом случае входными данными будет вывод предыдущей команды, а во второй — конкатенация содержимого файлов.


    CODE будет выполняться для каждой строки входных данных, которая будет записываться в переменную $_. Важно: строки будут без завершающего \n (за это отвечает флаг -l).


    Примеры


    Отраженный cat:


    ruby -lne 'puts $_.reverse' file-to-show.txt

    Вывести только первые 10 символов имени файлов в текущей директории:


    ls | ruby -lne 'puts $_[0..9]'

    Немного подробностей


    Предопределенные переменные


    В Ruby есть множество предопределенных переменных, о существовании многих из которых мы с вами можем даже не знать. Среди них есть и перекочевавшие из Perl, например, $_ и $< (на самом деле, в Perl нет такой переменной, но есть конструкция оператора ввода <>, читающая из файлов/stdin и устанавливающая $_).


    С их списком можно ознакомиться здесь.


    В рамках темы поста советую обратить внимание на вышеупомянутые $_ и $<, а помимо них на: $~, $1, $stdin, ARGV, $stdout, STDIN и STDOUT.


    Страшный секрет gets


    Возможно, многие использовали gets для того, чтобы запросить ввод с клавиатуры. Однако далеко не все знают, что он проверяет содержимое ARGV — аргументов командой строки.


    Когда ARGV непустой, наш gets считает, что там указан список файлов, берет первый из них и пытается прочитать строку оттуда.


    Возьмем скрипт:


    # myscript.rb
    puts gets

    И запустим его с помощью ruby myscript.rb. Как и ожидалось, программа ждет нашего ввода.


    А теперь запустим его вот так:


    ruby myscript.rb no-such-file

    Произойдет ошибка: файла "no-such-file" не существует. А теперь изменим скрипт:


    # myscript.rb
    ARGV.pop
    puts gets

    Теперь интерпретатор опять ожидает нашего ввода, т.к. с помощью pop мы выкинули наш "no-such-file".


    Опции интерпретатора


    Вот так выглядит мой шаблон однострочника:


    ruby -lne <CODE> [file1, file2, ...]

    (зачастую без -l)


    Описание используемых и некоторых потенциально полезных опций:


    • -e <CODE> — вынуждает интерпретатор не пытаться искать скрипт среди аргументов и выполнить CODE.
    • -n — оборачивает скрипт (или код, переданный с помощью -e) в цикл while gets. Метод gets неявно устанавливает возвращаемое значение переменной $_ (привет, Perl!), которую мы и должны использовать. Важно: строка будет заканчиваться \n.
    • -l — для простоты можно считать, что она просто удаляет символ новой строки \n из $_.
    • -p — работает точно так же, как и -n, но после каждой итерации выводит содержимое $_. Т.е. подразумевается, что вы будете его менять. Например: ls | ruby -pe '$_.upcase!'
    • -C <DIR> — меняет рабочую директорию.
    • -a(от auto-split) — используется совместно с -n или -p и устанавливает переменную $F, равной $_.split.
    • -F — устанавливает стандартный разделитель для String#split ($;). Теоретически, может быть полезно совместно с -a. Например, так можно обрабатывать простенькие .csv

    Заключение


    Впервые использовать ruby -e меня натолкнула команда cut. Мне ее приходилось пару раз использовать, и каждый раз мне нужно было открывать мануал, который моя тугая голова не хотела быстро понимать.


    В конечном итоге я подумал: "Да, я не умею пользоваться cut/awk/sed. Но зато я неплохо знаю Ruby, почему я не могу для всяких мелочей использовать его?".


    Собственно, в конечном итоге вместо запоминания множества команд мне достачно было запомнить только конструкцию, полученную опытным путем:


    ruby -lne CODE


    И все. Все мои проблемы с построчной обработкой текста решены.


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


    P.S. Напоследок вот пример использования:


    Скачал сезон сериала, а там имена файлов некрасивые, вроде "s01e01 — super release by super-mega-macho.mkv", хочу их переименовать, чтобы в медиатеке все было красиво. Пожалуйста:


    ls | ruby -lne 'File.rename($_, "#{$_[0..5]}.mkv")'
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 24
    • 0
      Просто подумал, что, возможно, не всегда удобно использовать _$ и -n. С тем же xargs например ls | ruby -lne 'puts $_[0..9]' можно переписать как ls | xargs ruby -e 'ARGV.each { |file| puts file[0..9] }'. Хотя конкретно для этого кейса ваш код выглядит поприятнее
      • 0

        Ну, в посте разбирается построчная обработка. Я сомневаюсь, что использование ARGV когда-нибудь будет удобнее в этом плане.
        Хотя все возможно:)

        • 0

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

          • 0
            ага, именно) разве что так сразу пример не подобрать, когда может понадобиться.
            • 0

              В любом случае я бы использовал $< вместо ARGV:


              ls | ruby -e '$<.each_with_index { |s, i| puts "#{i+1}: #{s}" }'
              
              # vs
              
              ls | xargs ruby -e 'ARGV.each_with_index { |s, i| puts "#{i+1}: #{s}" }'
              • 0
                $< выглядит как прекрасная альтернатива, да, xargs + ARGV будет полезен когда нужны все аргументы сразу, типа для случая ARGV.join или ARGV[0] + ARGV[-1], скорее не для построчной обработки, что вы в общем и сказали, конечно зависит от количества входных данных и всегда можно заюзать ARGF если захотеть, я бы сказал, что по ситуации надо выбирать
              • +1
                Можно распараллелить обработку:
                ls | xargs ruby -e 'require "parallel"; Parallel.each(ARGV) { |s| puts s.upcase }'
                • +2

                  Спасибо за идею!


                  можно написать -r parallel, чтобы не перенасыщать строку кода:


                  ls | xargs ruby -r parallel -e 'Parallel.each(ARGV) { |s| puts s.upcase }'
        • –1
          или можно просто научиться awk и sed, и особо много времени на это не нужно
          • +1

            А если Вам потребуется что-то мелкое, но выходящее за рамки знаний? Вы пойдете гуглить нужный инструмент и потом будете читать мануал к нему?
            Вы тогда изучать инструмент будете дольше, чем решать задачу (в единственном числе), для которой он Вам нужен.


            В случае с ruby/perl/etc Вы, запомнив по сути всего парочку фактов, получаете швейцарский нож, с помощью которого можете сделать, что угодно.


            Это хорошо, если человек знает awk+sed. Он решит задачу, скорее всего, красивее, чем с помощью руби/перл. Но это при условии, что он знает. Мне вот это пригождается не так часто, чтобы я постигал прелести awk (которым я раньше временами пользовался) и пытался держать их в голове.

          • 0

            Нет, не поймите неправильно, против ruby ничего не имею) сам на нем пишу скрипты где не хватает просто bash'a, например если нужно дроплетов на digitalocean создать пачку (что собственно можно и на bash с использованием curl сделать, но да, на ruby удобнее и короче) или выполнить действительно сложную обработку текста. Но ради реверс cat ruby вызывать дело такое. Тем более ruby по умолчанию далеко не везде присутствует, в отличии от awk и sed. Если уж вы работаете с unix like системами, не плохо бы изучить встроенные возможности, а не изобретать велосипеды имхо

            • 0

              Зачем что-то делать, если можно этого не делать?:)


              Нужно стараться сбалансировать изучаемую информацию. Я вот не хочу потратить на изучение чего-то полдня, чтобы потом все забыть и при надобности читать заново.


              Ruby я пользуюсь постоянно и забыть я ничего не могу. Логично, что если мне нужно выполнить какую-то мелкую задачу, я воспользуюсь им и получу результат мгновенно, а не буду курить маны весь день, чтобы одну строчку написать

              • 0

                Затем что половина таких программистов потом права на файл нормально выставить не могут, потому что "мне лень запоминать эти цифры, я знаю что с 777 у меня все работает"

                • 0

                  Не особо понимаю, как коррелируют изучение основ ОС *nix с изучением awk/sed.

                  • –1

                    Тем что это coretools, такой же как mv, ls и т.п.
                    Например если вам придётся делать те же мелкие задачи на удаленном сервере, где руби нет и установить не вариант

                    • 0

                      perl тоже есть практически везде.


                      И я с недоверием отношусь к аргументации вида "если вам придется". Для меня это не аргумент. Человек для себя сам расставляет приоритеты.
                      Летая на Камчатку я рискую потерпеть крушение где-нибудь в горах. Однако это для меня не повод изучать основы выживания в дикой природе.


                      Тут главное не перегнуть. Изучение awk/sed — это явный оверхед.


                      P.S. Я, кстати, изучал awk и даже использовал. Вот только спустя какое-то время знания уходят.

                      • –1

                        Perl да, ruby нет. Опустим sed и awk, из начала статьи понятно что у вас cat и его ключи уже вызывают оверхед. Вы find видели хоть раз? На понимание sed и awk, в рамках описанных вами кейсов, уходит 10 минут. А если вы действительно это поймёте то и запоминать особо ничего не нужно. Ах да, regexp же ещё нужно понимать, но если и тут мимо, то и разработчик вы посредственный.

                        • 0

                          Т.е. вы считаете, что нужно изучать перл?:)
                          У меня для вас плохие новости: не нужно.


                          При чем здесь cat? При том, что я привел пример вывода текста? Во-первых, это было просто ради того, чтобы показать работу параметров ruby, во-вторых, эту задачу невозможно решить только с помощью cat. Ну и вообще попахивает неуместной желчью.


                          find? видел. Использовал. Не использую.


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

                          • 0

                            Невозможно решить задачу с cat?) Про tac file.txt вы конечно не слышали.

                • +1

                  То что вы называете "курить полдня маны" на самом деле называется основами работы в командной строке) Видимо описанный вами лайфхак только и применяется чтобы сериалы у себя на компе переименовать.

                  • 0

                    Именно так! В этом и суть: для мелких задач, когда изучение proper tools — это оверхед

                    • –1

                      С таким подходом пишите на ruby в windows, Linux вам не нужен

                      • 0

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

                        • 0

                          Да, вы просто сеите дурную манеру. Курите лучше маны)

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