1. bash / Говнокод #8473

    −125

    1. 1
    2. 2
    3. 3
    4. 4
    {  
       (cd "$DIR"; ls -1);
       {  xml2 < $XML | grep '/list/files/@path=' | cut -d = -f 2 | cut -d / -f 2 | sort | uniq; }   
    } | sort | uniq --count | grep '^ *1' | awk '{print $2}' | (cd "$DIR"; xargs --no-run-if-empty rm -v)

    Удаляет из "$DIR" всё, что не описано в files.xml.

    Запостил: x28cnp, 10 Ноября 2011

    Комментарии (33) RSS

    • > grep '/list/files/@path='
      xmllint --xpath не берет?
      > uniq --count | grep '^ *1' | awk '{print $2}'
      uniq -u же
      > xargs --no-run-if-empty rm -v
      лучше ./'{}' (а то мало ли, что в $XML

      зы... 2 cd - тоже не очень, и есть `combine' из `moreutils' (правда, хотя бы один из входов должен быть файлом), sort | uniq -> sort -u (и в данном случае можно без него)
      Ответить
      • > и есть `combine' из `moreutils'

        я подобные задачи - пересечение (предварительно отсортированых) множеств - приучился diff делать, который спокойно башевы `<(cmd)` конструкцие пережевывает и более портабельный. по выводу дифа можно грепнуть что есть только в левой части (^<), или что есть только в правой части (^>).

        и вместо `xargs` который по жизни кучу граблей портабельности имеет (например в hp-ux), я просто на лету sed'ом генерю комманды и потом их пайпю в шелл. типа: "ls -1 | sed 's!^!rm -rf !' | sh" - в особенности со встроеными коммандами это работает на ура.
        Ответить
        • > башевы `<(cmd)` конструкцие пережевывает и более портабельный

          Всё-таки s/и/или/. В 1 случае и `combine' поймёт `process substitution'.
          Во 2 сойдет `comm', разве что -123 не интуитивны, и для надёжного OR или XOR (только в одном) хуже подходит.

          > "ls -1 | sed 's!^!rm -rf !' | sh"
          "| sh" да на левом входе... "touch tilda". Лучше system(), посиксовый awk вроде поддерживает, или обернуть:
          | while read line; do ...; done
          
          alias wdo='while read l; do'		# для командной строки
          ls | wdo echo "$PWD/$l"; done		# silly full-path
          
          alias -g W='|while {read l;}'		# для zsh
          ls W {echo $l:A}			# silly full-path
          +Профит в виде чтения переменных, для awk же лучше явно передавать.

          > работает на ура
          Почему-то :P вспомнилось: http://bestbash.org/b_25471
          Ответить
          • read -r на левом входе.
            Ответить
          • >> башевы `<(cmd)` конструкцие пережевывает и более портабельный

            потому что баш стоит почти везде - в отличии от GNU *-tools/*utils

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

            > "| sh" да на левом входе... "touch tilda". Лучше system(), посиксовый awk вроде поддерживает, или обернуть:

            трюк с '| sh' позволяет (если сперва убрать '| sh') преварительно увидеть какие комманды будут исполнятся и их отдельно протестировать.
            другим образом протестировать комманды использующие </> редиректы почти невозможно.

            > +Профит в виде чтения переменных, для awk же лучше явно передавать.

            да, да. но. как по мне почти все что `while read` может делать, можно сделать и без него. я стараюсь почти для всего найти вариант с пайпами что бы туда еще можно было и xargs всобачить. например удаление файлов по одиночке будет подтормаживать на больших количествах - с xargs'ом для групировки файлов заметно быстрее. и xargs совместим с '| sh' трюком :)

            например, сравни это:
            ls | while read l; do rm "$PWD/$l"; done

            с вот этим:
            ls | xargs -n 100 | sed 's!^!rm !' | sh


            пайплайнинг и паралелизация в полный рост. ;)
            Ответить
            • Привел же пример, вот еще один: touch "$'\x2f'star&echo применяется патч Бармина, ^C - покаяться"
              Ответить
            • .
              Ответить
            • > еще можно было и xargs всобачить
              Вы сидели на стуле "портабельность", что несколько ослабляет аргумент.

              > например удаление файлов по одиночке
              Именно в этом случае `find .. -delete'.

              > xargs совместим с '| sh'
              Не всегда хаrgs критичен (есть и find +), имхо, это того не стоит.
              Ответить
              • > > еще можно было и xargs всобачить
                > Вы сидели на стуле "портабельность", что несколько ослабляет аргумент.

                `xargs -n NNN` - это тоже POSIX.

                использование подстановки типа '-I {}' - которое тоже к слову POSIX - по моему опыту минное поле. потому что куча find'ов не правильно обрабатывает `-I` параметр если нужно вместо '{}' какую другую строку использовать.

                > этц про find, включая +

                насчет `+` мой первый же тест (минуту назад (-: ) показал проблему:
                "find: echo: Argument list too long". другими словами это неправильная замена xargs, т.к. xargs он в обе стороны работает: с одной стороны он агрегирует много строк в меньшее количество строк, с другой стороны он регулирует длину комманды что бы она за лимит не выходила.
                Ответить
                • > что куча find'ов не правильно обрабатывает `-I` параметр если нужно вместо '{}' какую другую строку использовать.

                  find и -I не очень понял, можно пример? Об этом ограничении: plus sign that follows an argument containing the two characters "{}" (всмысле аргумент должен быть последним).

                  > find: echo: Argument list too long
                  Не воспроизводится. Судя по ману и проверке тоже вызавает несколько раз. Но буду иметь ввиду.
                  Ответить
                  • > find и -I не очень понял, можно пример?

                    оопс. это я стормозил и попутал с совсем не той проблемой. `-I` это у xargs, а не find. и эта проблема тут ни причем.

                    ЗЫ xargs на некоторых платформах если указать `-I` параметр перестают подставлять имена файлов вообще и просто комманду буквально как написано выполняют.

                    > > find: echo: Argument list too long
                    > Не воспроизводится

                    это у тебя в каталоге мало файлов. напусти чего-нибудь такое:
                    for X in `seq 1 10000`; do echo  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$X; done

                    и попробуй еще раз, например с "find $PWD" а не просто "find ."

                    фишка `find -exec +` что он запускает команду один раз на весь подкаталог. если в каталоге слишком много файлов, то длина команды легко переваливает за лимит (в общем случае 32К) и команда не выполняется.
                    Ответить
                    • Какой-то неправильный у вас find.

                      $ for X in `seq 1 10000`; do touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$X; done
                      $ echo * | wc
                            1   10000  458894
                      $ find | wc
                        10001   10001  478896
                      $ find -exec echo '{}' + | wc
                            4   10001  478896

                      Из последней команды видно, что echo выполнилось 4 раза.
                      Ответить
                      • > Какой-то неправильный у вас find.

                        не уверен. глянь в свой /usr/include/limits.h и посмотри чему равно ARG_MAX. (на линухе - сразу в /usr/include/linux/limits.h)

                        > $ echo * | wc

                        хехе. старая шутка. попробуй /bin/echo. echo вызываемое на прямую из шелла есть встроеная комманда - и поэтому обходит системные ограничения.

                        > $ find -exec echo '{}' + | wc

                        если запустить на моем линухе `find -exec echo '{}' + >/dev/null` первые три строки выглядят так:
                        find: echo: Argument list too long
                        find: echo: Argument list too long
                        find: echo: Argument list too long
                        и wc показывает неправильные цифры.

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

                        самый прикол что на моем SLES 10.2 ARG_MAX самый маленький - 128K. на 10й солярке 2MБ. на AIX официально 24К, но я не вижу что бы этот лимит какой-то роли играл. HP-UX - 1MB.

                        у тебя на системе ARG_MAX стоит в минимум 512К

                        вообщем разброд и шатание, ну да я уже привык это ограничение обходить.
                        Ответить
                        • Там ключевое было: > Из последней команды видно, что echo выполнилось 4 раза.

                          У меня было так (лимит вроде 128k):
                          /bin/echo /media/*/*/*/*/*/*
                          bash: /bin/echo: Argument list too long
                          find /media -maxdepth 6 -mindepth 6 -exec echo '{}' +
                          выводит
                          Ответить
                        • ARG_MAX у меня равен 131072 — как раз чтобы разбить 458894 байт на 4 строки.

                          А find у вас какой-то кривой.
                          $ find --version | head -1
                          find (GNU findutils) 4.4.2
                          $ `which echo` --version | head -1
                          echo (GNU coreutils) 7.4
                          Ответить
                        • Впрочем, здесь не в ARG_MAX дело. Какой у вас ulimit -s? Проблемы начнутся около 512.
                          Ответить
                          • в это ARG_MAX и есть проблема.

                            размер стека (главного потока) как правило большой (и у меня 8МБ) что бы его так легко переполнить.

                            ARG_MAX как лимит существует потому что эти данные - коммандная строка и окружения - копируются в exec() через кернел в образ нового процесса. а кернела они любят что бы на все буфера было какое-то вразумительное ограничение, т.к. кернелова память не свапится.

                            да и если процесс стек переполнит, то он будет просто убит каким SEGV. красивых сообщениях об ошибках ты не увидишь.
                            Ответить
                            • Преблема, если ARG_MAX больше четверти лимита стека. find, очевидно, режет на строки по ARG_MAX, а реальный лимит — четверть стекового (не думаю, что у вас версия меньше 2.6.23).
                              Ответить
                      • http://www.in-ulm.de/~mascheck/various/argmax/

                        я смотрю трюков для обхода ограничения намного больше чем я предполагал. и кое что из того что я думал не рабоатет - на самом деле работает.
                        Ответить
        • Нафига diff, когда есть каноничный посиксовый join?
          Ответить
    • О, grep уже научился XPath? Интересный у них XML.
      Ответить
      • xml2 просто удобная штука, как poor man's xpath: смотришь что она выводит и без справочников сразу знаешь что тебе нужно. Есть ещё 2xml, html2, 2html.
        Ответить
    • О, давно я половыми извращениями не страдал. Надо будет попарсить XML на gawk'е.
      Ответить
      • это же чисто некрофилия!
        Ответить
        • Неправда. GAWK можно использовать не только для того, чтобы вывести третью колонку данных и передать её дальше в греп.
          Бывший босс делал какую-то свою хитрую систему, ему там было нужно сделать nslookup множества доменов, получение уникальных строк и засовывание айпишников в файервол. Я небольшой кусочек помог, но там вышел какой-то дикий скрипт на баше. Ради интереса посидел два часа, переписал полностью на gawk. Офигенная штука, скажу я. И с текстами работать умеет. И как-то там парсить. Самое то.
          Ответить
      • показать все, что скрытона bash орк
        Ответить
    • показать все, что скрытоvanished
      Ответить

    Добавить комментарий