Однажды в телеграм чат питерского сообщества линуксоидов SPbLUG я кинул забавную задачку:
Выведите список файлов в домашней директории максимально возможным количеством способов, без использования ls или его алиасов(1 способ — 1 балл)

Такое же задание чуть позже прилетело в ещё один чат, и вот что из этого получилось:

1. echo и print


for i in ~/*  ~/.* ; do echo $i ; done

Ровно то же самое выдаст замена команды echo на print.
На самом деле можно обойтись и без цикла, получится не так красиво, но под условие задачи вполне подходит.

echo ~/* ~/.*

2. tree


Более очевидный способ — использовать tree, который практически ls если подобрать правильные ключи.

tree -aiL 1 ~

3. find


Тоже более чем очевидное решение.

find ~ -maxdepth 1 -mindepth 1

4. du


Да, про du народ не забыл.

du -ad 1 ~

5. tar


Переходим к водным процедурам тонким извращениям.

tar -cvf /dev/null --no-recursion ~/* ~/.* 2>null

6. 7. Perl и Python


Так как в условии задачи я забыл поставить ограничение на интерпретаторы, которые в современных линуксах обычно есть в системе из коробки, то кашевары и змееводы не смогли остаться в стороне:
Perl:

perl -e 'use feature "say"; opendir my $dh, "." or die "Could not open . for reading: $!\n"; while (my $thing = readdir $dh) { say $thing; };'

Python:

echo -e "import os\nfor i in os.listdir(os.getenv('HOME')): print(i)" | python

Вне конкурса


Выдали на гора даже исходник на C, но хоть компилятор и присутствует практически везде, кроме всяких emmbedded дистрибутивов, я посчитал это уж совсем полным беспределом. ;-)

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

#define HOME getenv("HOME")

int main(int argc, char const *argv[])
{
    struct dirent *dp;
    DIR *dir = opendir(HOME);
        while ((dp = readdir(dir)) != NULL)
            printf("%s\n", dp->d_name);
    closedir(dir);

    return 0;
}

P.S.


Вероятно, где-то в coreutils/findutils участники развлекухи что-то пропустили. Были неудачные попытки использовать less/more, но может у хабровчан тоже появились идеи по нестандартному использованию стандартных утилит.

Upd. 1


Спасибо хабр! Комменты, это просто праздник какой-то! И по их мотивам я готовлю новый пост. Оставайтесь на связи!

Upd. 2


Как я и обещал, вторая часть «Марлезонского балета».

Комментарии (128)


  1. m03r
    11.09.2019 07:52

    cd ~; vi .
    (zsh) ~/ [tab][tab]


    1. SlavniyTeo
      11.09.2019 13:16

      [tab][tab] — слишком интерактивно


      echo -e 'echo \t\ty\b\b\b\b\b\b\b' | bash -i

      К сожалению, выведет пару лишних строк.


  1. eumorozov
    11.09.2019 08:12

    Конечно пропустили. В coreutils, например, есть команда dir. :)


  1. cru5ader
    11.09.2019 08:22

    dir ~


  1. cru5ader
    11.09.2019 08:25

    vim .


  1. 027
    11.09.2019 09:26
    +1

    cp ~/ <Tab><Tab>
    mv ~/ <Tab><Tab>
    dd if=~/ <Tab><Tab>
    и т.п.


    1. MaxVetrov
      12.09.2019 21:39

      можно несуществующую команду:
      a ~/[TAB][TAB]
      ! ~/[TAB][TAB]
      можно для текущей директории такое:
      ![TAB][TAB][TAB]

      PS. Пока эксперементировал, нашел devbash :)
      `/[TAB][TAB]

      выдает:

      binbash: bad substitution: no closing "`" in `/bin
      etcbash: bad substitution: no closing "`" in `/etc
      lib64bash: bad substitution: no closing "`" in `/lib64
      mntbash: bad substitution: no closing "`" in `/mnt
      runbash: bad substitution: no closing "`" in `/run
      tmpbash: bad substitution: no closing "`" in `/tmp

      bootbash: bad substitution: no closing "`" in `/boot
      homebash: bad substitution: no closing "`" in `/home
      libx32bash: bad substitution: no closing "`" in `/libx32
      optbash: bad substitution: no closing "`" in `/opt
      sbinbash: bad substitution: no closing "`" in `/sbin
      usrbash: bad substitution: no closing "`" in `/usr

      cdrombash: bad substitution: no closing "`" in `/cdrom
      libbash: bad substitution: no closing "`" in `/lib
      lost+foundbash: bad substitution: no closing "`" in `/lost+found
      procbash: bad substitution: no closing "`" in `/proc
      srvbash: bad substitution: no closing "`" in `/srv
      varbash: bad substitution: no closing "`" in `/var

      devbash: bad substitution: no closing "`" in `/dev
      lib32bash: bad substitution: no closing "`" in `/lib32
      mediabash: bad substitution: no closing "`" in `/media
      rootbash: bad substitution: no closing "`" in `/root
      sysbash: bad substitution: no closing "`" in `/sys


      1. 027
        12.09.2019 21:45

        Я за эти клавиатурные таб-таб каюсь, ибо совсем забыл, что:

        • автоподстановка не является всеобщим правилом для *никсов, а есть только там, где она сознательно реализована мейнтейнером для конкретного окружения конкретной версии*;
        • есть способ посмотреть без выстукивания по клаве, что выдаст автоподстановка в разных ситуациях, а именно — команда баша compgen.

        *) например, автодополнение в моей системе живет тут: /etc/bash_completion и тут: /etc/bash_completion.d.


        1. MaxVetrov
          13.09.2019 21:26

          Заинтересовался автодополнением.)
          А с помощью complete можно эту задачу решить?


  1. POPSuL
    11.09.2019 09:46

    Так конечно делать не нужно, но если уж совсем нет других вариантов…

    Я предупреждал
    yes "n" | rm -i $HOME/* $HOME/.* 2>&1 | grep -o "$HOME[^»'’]*"
    


    1. shanlove
      11.09.2019 16:30

      Можно и чуть покороче))
      rm -fv ~/* 2> /dev/null


      1. Oxyd Автор
        12.09.2019 03:36

        Ну и шуточки у вас. ;-)


    1. 027
      11.09.2019 19:56

      Я предупреждал

      Но мы же все делаем бэкап $HOME, правда?


      1. orthanner
        13.09.2019 07:50

        Конечно. В $HOME/.backup :-)


        1. 027
          13.09.2019 20:15
          +1

          А-а-а! Ты знал!!!

          $ ls -1 ~
          ...
          027
          1
          backup
          projects
          scripts
          Видео
          Документы
          ...
          


          1. SlavniyTeo
            15.09.2019 08:40

            Я не осуждаю, но в этом топике листинг домашней директории можно было привести и без использования ls


            1. 027
              15.09.2019 08:42

              А-а-а! Ты знал!!!


  1. Pilat
    11.09.2019 09:46

    Самый простой способ — создать архив файлов в нужной директории а потом вывести листинг архива. Это поддерживает куча архиваторов. Можно и рекурсивно.


    1. vladkorotnev
      11.09.2019 10:21

      Что-то подобное через tar как раз и описано, только листинг выводится при упаковке


  1. deniss-s
    11.09.2019 09:54

    grep "" -lr .
    less .
    


    1. 027
      11.09.2019 10:06

      Вышенаписанные варианты

      $ less .
      $ vi .
      у меня выводят:
      . is a directory

      grep "" -lr .
      выводит все потроха рекурсивно (не уверен, что это соответствует условию задачи)


      1. deniss-s
        11.09.2019 10:14

        Странно, что less себя так ведёт, может зависит от шела? У меня в bash показывает содержание текущей директории.


        С grep можно попробовать такой вариант grep "" -l ./*, правда формат вывода директорий, не очень красив.


        1. 027
          11.09.2019 10:22

          Странно, что less себя так ведёт, может зависит от шела?

          Наверное. У меня
          $ bash --version
          GNU bash, версия 4.4.20(1)-release (x86_64-pc-linux-gnu)
          $ lsb_release -d
          Description:	Linux Mint 19.1 Tessa


          «nano .» то же самое пишет, разве что по-русски.


          1. deniss-s
            11.09.2019 10:38

            У меня в Fedora работает, а у коллеги на Mint нет. :)


            1. 027
              11.09.2019 10:41

              Стало быть, способ не универсален, только и всего.


          1. eumorozov
            11.09.2019 12:18

            Проверять надо при помощи type less (у меня fish, не помню, как в bash записывается, кажется так же) и less -V.


          1. DerRotBaron
            11.09.2019 14:40

            И от шелла и не от шелла. В Fedora переменная LESSOPEN определяется на скрипт, выводящий в случае директории вывод ls.


            Детали
            [user@localhost ~]$ rpm -ql less
            /etc/profile.d/less.csh
            /etc/profile.d/less.sh
            /usr/bin/less
            ...
            /usr/bin/lesspipe.sh
            ...
            [user@localhost ~]$ cat /etc/profile.d/less.sh 
            # less initialization script (sh)
            # All less.*sh files should have the same semantics!
            if [ -z "$LESSOPEN" ] && [ -x /usr/bin/lesspipe.sh ]; then
                # The '||' here is intentional, see rhbz#1254837.
                export LESSOPEN="||/usr/bin/lesspipe.sh %s"
            fi
            [user@localhost ~]$ cat /usr/bin/lesspipe.sh                
            #!/bin/sh
            ...
            # The script should return zero if the output was valid and non-zero
            # otherwise, so less could detect even a valid empty output
            # (for example while uncompressing gzipped empty file).
            # For backward-compatibility, this is not required by default. To turn
            # this functionality there should be another vertical bar (|) straight
            # after the first one in the LESSOPEN environment variable:
            # export LESSOPEN="||/usr/bin/lesspipe.sh %s"
            ...
            if [ -d "$1" ] ; then
                    ls -alF -- "$1"
                    exit $?
            fi
            ...


  1. 027
    11.09.2019 09:59

    rsync --list-only ~/


  1. kashtan404
    11.09.2019 10:10

    Я считаю, grep и stat незаслуженно забыли:

    grep -l '.*' ./*
    stat ./*
    


    1. 027
      11.09.2019 10:46

      stat незаслуженно забыли

      stat дает чрезмерно говорливый выхлоп, а в задаче надо просто список файлов.
      Тогда уж лучше file:
      file ~/*

      Но этот способ не показывает скрытые файлы (как и все иные, основанные на разворачивании звездочки интерпретатором). Может, как-то можно через переменные окружения на это повлиять?


      1. POPSuL
        11.09.2019 12:23

        file ~/* ~/.*
        


        1. 027
          11.09.2019 12:34

          Ну или так.


          1. iig
            11.09.2019 17:53

            file  -0 * |cut -f 1 -s -d ''


      1. polar_yogi
        11.09.2019 16:48

        Говорливость stat регулируется, можно выводить от только имени до хотя json какого нибудь.
        Например:
        stat -c "%A %G:%U %s %x %n" * \.*


      1. bananaseverywhere
        11.09.2019 18:56

        shopt -s dotglob
        включит скрытые файлы в маску *


      1. Oxyd Автор
        12.09.2019 04:35

        Ну говорливый. Говорилка-то угоманивается… ;-)

        stat ~/* ~/.*|grep "/home"|awk '{print $2}


        1. 027
          12.09.2019 05:13

          Ага, точно. Будто слишком говорливой теще заткнули рот кляпом, а когда это не помогло, стали душить удавкой. :)
          Удawkой, кхм…


          1. Oxyd Автор
            12.09.2019 05:24

            Мы их душили, душили! Душили, Душили!
            © Шариков.


    1. KorDen32
      11.09.2019 12:49

      grep так не покажет пустые файлы

      grep -l '.*' ./* || grep -L '.*' ./*


  1. deniss-s
    11.09.2019 10:18

    gzip -cv1 ./* | gzip -l


    1. Oxyd Автор
      12.09.2019 04:37

      Что-то он у меня в хомяке чуть ли не треть файлов не показал…


  1. Artyushov
    11.09.2019 10:32

    head -n 0 -v ./*


  1. Stronix
    11.09.2019 12:04

    echo -e «import os\nfor i in os.listdir(os.getenv('HOME')): print(i)» | python

    Так аккуратнее, на мой взгляд:
    python -c "import os;  print(os.listdir('.'))"


  1. RumataEstora
    11.09.2019 13:14

    1. echo и print

    Эти команды не идентичны. Возможно Вы имели в виду printf, но она может сбоить в некоторых случаях, хотя echo — тоже.


    1. tar

    У вас мешанина в примере — /dev/null и null.


    1. Perl

    Нет нужды использовать последние фичи языка, но можно использовать внутренние возможности:


    perl -le '$d = "."; opendir D, $d or die "Could not open $d for reading: $!\n"; print while (readdir D)'

    И, кстати, вот еще один перловый ls:


    perl -le 'print while <.* *>'


    1. azmar
      11.09.2019 14:59

      На случай если есть руби из коробки:
      ruby -e 'puts Dir.entries "."'


      1. 027
        11.09.2019 20:25

        На случай, если есть php в коробке.

        Для текущей:

        php -r 'print_r(scandir("."));'

        Для домашней:
        php -r 'print_r(scandir($argv[1]));' -- ~

        Для всякой:
        php -r 'print_r(scandir($argv[1]));' -- <some_dir>


        1. azmar
          11.09.2019 22:23

          Интересно есть ли дистрибутив, в котором пхп прямо в базовой поставке)


          1. 027
            11.09.2019 22:28

            Любой, снабженный веб-мордой на php. Openmediavault, например.


  1. rionnagel
    11.09.2019 13:48

    [любые символы][space][tab][tab]
    GNU bash, version 5.0.7(1)-release
    urxvt v9.22


    1. 027
      11.09.2019 13:58

      любые

      У меня, как минимум, не работает с кавычками и апострофом.


      1. rionnagel
        11.09.2019 13:59

        Я утрировал. Не совсем любые.


      1. 027
        11.09.2019 14:03

        О, символы вообще не нужны! Достаточно просто пробела.


        1. rionnagel
          11.09.2019 14:10

          У меня не воспроизводится (


          1. 027
            11.09.2019 14:31

            Зависит от окружения, стало быть. Сейчас потыкал на контейнере с убунтой 14.04 — совсем не так реагирует на колочение таба. Плохой способ.


  1. Zoro
    11.09.2019 14:20
    +1

    getfacl ~/* ~/.*


    1. Zoro
      11.09.2019 15:36

      getfacl ~/* ~/.* | grep "# file" | awk '{print $3}'


  1. Zoro
    11.09.2019 15:06
    +1

    ls -i /home
    529589 test

    debugfs /dev/sda1
    debugfs: stat <529589>
    Inode: 529589 Type: directory Mode: 0755 Flags: 0x80000
    Generation: 2678704405 Version: 0x00000000:00000007
    User: 1000 Group: 1000 Project: 0 Size: 4096
    File ACL: 0
    Links: 4 Blockcount: 8
    Fragment: Address: 0 Number: 0 Size: 0
    ctime: 0x5d721b09:c9c4305c — Fri Sep 6 08:38:33 2019
    atime: 0x5d78e0f5:a027932c — Wed Sep 11 11:56:37 2019
    mtime: 0x5d721b09:c9c4305c — Fri Sep 6 08:38:33 2019
    crtime: 0x5d721abd:243d5800 — Fri Sep 6 08:37:17 2019
    Size of extra inode fields: 32
    Inode checksum: 0xa0aa0f22
    EXTENTS:
    (0):2106181

    берем
    (0):2106181

    dd if=/dev/sda1 of=image.dd bs=4096 count=1 skip=2106181

    hexdump -C image.dd | awk '{print $18}'
    |................|
    |................|
    |.profile........|
    |.bashrc.........|
    |.bash_logout....|
    |.....cache......|
    |.....gnupg......|
    |.....sudo_as_adm|
    |in_successful...|
    |................|

    |............&..W|

    :)


    1. Oxyd Автор
      12.09.2019 04:45

      Круто! Реально круто! Осталось понять как избавиться от ls -i…


      1. POPSuL
        12.09.2019 06:13

        Можно через stat узнать inode


  1. iig
    11.09.2019 15:32
    +1

    stat *|awk '/File:/{print $2}'


    1. Oxyd Автор
      12.09.2019 05:03

      Хорошо, до той поры пока не наткнётся на русскую локаль… ;-) Ну или например китайскую.


      1. POPSuL
        12.09.2019 06:14

        LANG=C stat ... никто не отменял :)


        1. 027
          12.09.2019 07:21

          О, кстати!
          Кто объяснит, почему в подобных конструкциях после задания переменной не нужно ставить точку с запятой? Это же отдельная команда, по идее.


          1. POPSuL
            12.09.2019 11:37

            Нет, это изменение environment variable для запускаемой программы, а не глобально. именно поэтому при "пайпинге" нужно указывать для каждой команды свои переменные окружения (ну, если нужно конечно), вроде


            LANG=C stat . | LANG=C other-command


            1. 027
              12.09.2019 11:40

              Спасибо. А не подскажете, где про это можно почитать? Это же не bash-специфичное поведение?


              1. POPSuL
                12.09.2019 12:32

                Это работает даже в sh.
                Почитать об этом можно в разделе https://help.ubuntu.com/community/EnvironmentVariables#Bash.27s_quick_assignment_and_inheritance_trick
                Ну и вообще, вся статья полезна.


              1. simpleadmin
                12.09.2019 14:09
                +1

                Это Bourne shell-совместимое поведение.
                Не будет работать в C-shell'ах
                Там потребуется выполнять env, set или setenv соответственно для изменения переменной окружения, установки сессионной переменной и экспорта переменной.


                1. eumorozov
                  12.09.2019 17:38

                  И в fish тоже по другому работает: env LANG=C ls
                  Я уже несколько лет как перешел с bash и zsh на fish. Не знаю откуда у меня такая настойчивость, первые года два ломало, а теперь втянулся и нравится.


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


                  1. Oxyd Автор
                    13.09.2019 04:44

                    А zsh-то в чём провинился?


                    1. eumorozov
                      13.09.2019 06:35

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


                  1. 027
                    13.09.2019 21:51

                    Я уже несколько лет как перешел с bash и zsh на fish. Не знаю откуда у меня такая настойчивость, первые года два ломало, а теперь втянулся и нравится.

                    Два года? Похоже, эта ваша рыба заливная! :)


                    1. eumorozov
                      15.09.2019 12:23

                      Честно говоря, единственная вещь, которая напрягала эти два года — отсутствие Ctrl-R в fish. Точнее, неточная эмуляции этого механизма при помощи fzf.


                      Когда работаешь с bash где-то с 1996 года, всякие bash'евские приемы работы начинают жить где-то уже в пальцах. Но два года оказалось достаточно, чтобы перебить старые привычки.


                      Все-такие fish — это какой-то шаг вперед. bash и zsh кажутся отставшими от времени и слишком эклектичными. PowerShell кажется наоборот очередным неудобным overengineering от Microsoft.


                      fish ­— этакий разумный компромисс между эклектичной олдфажностью и современностью.


            1. 027
              12.09.2019 14:37

              Хм… А почему вот это не работает?

              $ LANG=C echo $LANG
              ru_RU.UTF-8
              

              $ LANG=C /bin/echo $LANG
              ru_RU.UTF-8
              


              1. POPSuL
                12.09.2019 14:46

                Потому что $LANG это переменная, которая уже объявлена, и при выполнении этой команды она "разворачивается" в значение ru_RU.UTF-8.


                upd: именно поэтому я и показал пример с getenv


                1. 027
                  12.09.2019 15:10

                  Потому что $LANG это переменная, которая уже объявлена, и при выполнении этой команды она «разворачивается» в значение ru_RU.UTF-8.

                  Как же так? Я ее только что переопределил из 'ru_RU.UTF-8' в 'C'.
                  Почему-то echo это игнорирует, а вот, скажем, man, radeontop используют значение 'C'.


                  1. POPSuL
                    12.09.2019 15:17

                    Потому-что в баше переменные "разворачиваются" в значение до запуска самой программы. По схожему принципу и работает glob, т.е. вы напишете cat *, но баш на самом деле сам прочитает содержимое директории, и выполнит что-то вроде cat a.txt b.txt c.txt somedir.
                    Почему radeontop работает — я не знаю, возможно он работает совсем не так как вы ожидаете, и просто не замечаете это (я за зеленых, поэтому такие тулзы ни разу в жизни не использовал).


                  1. simpleadmin
                    12.09.2019 15:45
                    +1

                    Почему-то echo это игнорирует

                    Именно так, потому что echo не «интересуют» переменные окружения.
                    Пример
                    EDITOR=mcedit crontab -e
                    откроет cron в mcedit
                    EDITOR=vi crontab -e
                    откроет cron в vi
                    но
                    $ EDITOR=vi echo "one: [$EDITOR]"
                    one: []

                    echo просто пофиг на нашу переменную окружения.
                    Но мы можем задать её как переменную оболочки и ограничить область действия (Bourne shell-совместимое поведение):
                    $ (EDITOR=vi; echo "one: [$EDITOR"]); echo "two: [$EDITOR]"
                    one: [vi]
                    two: []

                    аналогично для запрашиваемого примера:
                    $ (LANG=C; /bin/echo $LANG); /bin/echo $LANG
                    C
                    en_US.UTF-8



                    1. 027
                      12.09.2019 17:22

                      Именно так, потому что echo не «интересуют» переменные окружения.

                      Я понял так, что модифицированные переменные окружения выдаются программе в виде копии и исключительно для нужд настройки поведения.
                      $LANG интерпретатор берет из текущего окружения для всего сеанса ( а echo тут вообще ничего не решает).
                      Если бы удалось заставить echo ругаться на что-либо, тогда модифицированное значение переменной LANG влияло бы на язык сообщений. Как это и происходит в моей системе с man, nmap и radeontop, которые обучены русскому.

                      Трюк, походу, не bash-специфичный — в dash переключение языка срабатывает.


                      1. eumorozov
                        12.09.2019 17:43

                        echo — встроенная команда bash (builtin). То есть, при выполнении echo не будет порожден новый процесс, в котором будет запущена программа /bin/echo.


                        LANG=C влияет на порожденные процессы. Например, LANG=C ls приводит к порождению процесса запускающего /bin/ls, и в этом процессе уже будет модифицированное окружение.


                        1. 027
                          12.09.2019 18:12

                          echo — встроенная команда bash (builtin). То есть, при выполнении echo не будет порожден новый процесс, в котором будет запущена программа /bin/echo.

                          Я приводил в исходном каменте оба варианта, со встроенным эхом и с /bin/echo. Поведение одинаковое.

                          встроенная команда bash… не будет порожден новый процесс

                          Вы это точно знаете или предполагаете?


                          1. 027
                            12.09.2019 18:19

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


                          1. eumorozov
                            12.09.2019 18:24

                            Ах, да, прав комментатор про то, что bash подставляет значения переменных при вызове. Конечно, когда мы пишем cmd $VAR любой shell обязан подставить $VAR до вызова команды.


                            Вы это точно знаете или предполагаете?

                            Конечно уверен. На то они и builtins, к чему тратить ресурсы системы на порождение нового процесса?..


                            Поэтому при отладке каких-то проблем со скриптами и т.п., что я делаю первым делом — запускаю type ls или type cat и т.д., чтобы знать точно, что не имею дело с алиасом или командой, которая взялась не из стандартного пути.


                            1. 027
                              12.09.2019 18:30

                              Конечно уверен. На то они и builtins, к чему тратить ресурсы системы на порождение нового процесса?..

                              Уверенность на основе рассуждения есть предположение. :) Ну да бог с ним. Дело тут совсем не в отдельности процесса.

                              Ах, да, прав комментатор про то, что bash подставляет значения переменных при вызове. Конечно, когда мы пишем cmd $VAR любой shell обязан подставить $VAR до вызова команды.

                              Отож! Он идет по строке справа налево, натыкается на $LANG, рожает указатель (наверное, я в сях не силен) и тупо скармливает его эху. До модифицированного одноразового окружения ему дела нет.
                              А внешне кажется, что это эхо игнорирует, зараза такая своенравная. :)


                              1. eumorozov
                                12.09.2019 18:36

                                Уверенность на основе рассуждения есть предположение. :) Ну да бог с ним. Дело тут совсем не в отдельности процесса.

                                Ну, если бы shell не использовал builtins, то, например, такая команда как cd не имела бы смысла: она бы меняла только текущий каталог порожденного процесса, и в shell не было бы никакой возможности перейти в другой каталог.


                                А так полагаю, что существует много нюансов. Когда-то я целиком читал документацию по bash. Но это было наверное 20 лет назад, и много мелких нюансов я уже забыл. Возможно, для каких-то команд builtins используются только в интерактивном режиме. Беглый просмотр man bash по диагонали не дал подтверждения, надо внимательно читать его целиком, а он огромный.


                            1. 027
                              12.09.2019 18:36

                              запускаю type ls или type cat и т.д., чтобы знать точно, что не имею дело с алиасом или командой, которая взялась не из стандартного пути.

                              Кстати, есть оч. полезная команда в баше:
                              compgen -c will list all the commands you could run.
                              compgen -a will list all the aliases you could run.
                              compgen -b will list all the built-ins you could run.
                              compgen -k will list all the keywords you could run.
                              compgen -A function will list all the functions you could run.
                              compgen -A function -abck will list all the above in one go.

                              В частности, вот так можно отловить дублирующуюся команду:
                              $ compgen -c | grep -E '^time$'
                              time
                              time

                              Попалась, зараза!


                              1. simpleadmin
                                12.09.2019 21:29

                                $ type -a time
                                time is a shell keyword
                                time is /usr/bin/time
                                


                                1. 027
                                  12.09.2019 21:31

                                  О!
                                  Сенькс.
                                  Неисчислимы заковыристые ходы, придуманные в те давние времена, когда компьютеры были большими, а сообщество маленьким.
                                  Множество частных, подробно ориентированных штучек-дрючек, и никто не ведал, что скоро придет диавол под личиной multimedia, и пожрет все, до чего дотянется мягкими, липкими, необъятными в своих слоях абстракций лапищами…


                        1. MaxVetrov
                          12.09.2019 18:25

                          у меня echo — файл в bin


                          1. eumorozov
                            12.09.2019 18:29

                            Да, она есть во всех системах из каких-то соображений. Полагаю, чтобы в пользовательских программах не ломался код типа:
                            system("echo Hello world").


                            Но в shell запустится builtin. /bin/echo запустится, только если писать полный путь: /bin/echo Hello world.


                            1. 027
                              12.09.2019 18:43

                              Да, она есть во всех системах из каких-то соображений. Полагаю, чтобы в пользовательских программах не ломался код типа:
                              system("echo Hello world").

                              Если в путях есть /bin ничего не сломается. А так должно быть обязательно. Ну, если это не наколенная эмбедовка от васяна, понятно.

                              Соображения я встечал такое: большее быстродействие и меньший жор. Вызов внешней утилиты в любом случае накладно. К тому же встроенные нередко более убогие и потому скоростные.


                            1. MaxVetrov
                              12.09.2019 19:27

                              Действительно ,)
                              Для printf, kill, pwd, test — тоже самое.

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


                          1. 027
                            12.09.2019 18:48

                            у меня echo — файл в bin

                            У меня и то, и другое:
                            $ compgen -c | grep -E '^echo$'
                            echo
                            echo
                            
                            $ which echo
                            /bin/echo
                            
                            $ type echo
                            echo — это встроенная команда bash


                            1. MaxVetrov
                              12.09.2019 20:27

                              возможно, было бы удобно их с индексом запускать:
                              echo[0]
                              echo[1]


                              1. 027
                                12.09.2019 20:31

                                Это как?


                                1. MaxVetrov
                                  12.09.2019 23:54

                                  Я имею ввиду, если бы баш позволял.
                                  [0] — внутренняя
                                  [1] — внешняя
                                  Ключевое слово — возможно.

                                  Хотя есть команда command -p


                                  1. eumorozov
                                    13.09.2019 06:40

                                    Shell и так позволяет запускать любой вариант, было бы странно иначе.
                                    Просто echo — запускает builtin. /bin/echo запускает /bin/echo.


                                    Если кто-то или что-то переопределило echo, то builtin echo явно запустит builtin.


  1. lorc
    11.09.2019 19:41

    git init . ; git status

    Будет немного лишнего, но можно отфильтровать.


    1. 027
      12.09.2019 07:25

      Чуток поправлю.

      git init . ; git status; rm -rf .git


  1. klirichek
    11.09.2019 19:57
    +1

    Очень простая, но реальная задача:
    веб-камера с записью "сошла с ума" и вместо записи по движению скинула на флэшку непрерывную запись целой недели. ~20 тыс. файлов. ФС fat32.
    ls (просто) в папке DCIM ничего не выводит (думает минут 5, потом убивается системой).
    И вот тут выясняется, что ls написан внутри очень неплохо! Он не получает "список всего", а потом накладывает фильтр, а сразу фильтрует при выводе на экран. В итоге ls "20190901*" и т.д. вполне сработали! (последующий rm так же сработал по тому же паттерну)


    1. alhimik45
      11.09.2019 21:45

      Только это не ls и rm такие крутые, а шелл, который развернул имя со звездочкой в список файлов и уже их передал списком аргументов (и тут кстати можно словить ошибку, если файлов больше, чем максимально разрешенное число аргументов командной строки)


      1. 027
        11.09.2019 22:30

        Двойные кавычки спасут отца русской демократии, как в примере выше.


    1. azmar
      11.09.2019 22:25

      Рекомендую вам использовать утилиту find в подобных случаях.



  1. Cheater
    11.09.2019 19:59
    +1

    Даём команду на интерактивное удаление /home/username (rm -i), и генерируем для неё "интерактивный" ответ сначала из одного "y" (для подтверждения рекурсивного спуска в ~), и затем в цикле "n" (для отказа рекурсивного спуска на более нижние уровни и отказа удаления):


    (echo y; yes n) | rm -ir ~

    Улучшенный вариант с последующей чисткой лога от ненужного текста:


    (echo y; yes n) | rm -ir ~ 2>&1 | sed -E -e "s/(rm:[^']+)|\?//g"


    1. Cheater
      11.09.2019 20:31

      Upd: Извиняюсь, проглядел такое же решение в более раннем комменте под спойлером


      1. Oxyd Автор
        12.09.2019 05:12

        Зато sed ещё пока никто не использовал. ;-)


  1. facha
    11.09.2019 20:43

    hdfs dfs -ls


  1. 027
    11.09.2019 21:36
    +1

    без использования ls

    «Ты видишь суслика? И я не вижу.»
    $'\x6c\x73' $'\x2d\x61'


    1. Oxyd Автор
      12.09.2019 05:14

      Не тот человек взял ник Cheater ;-)


  1. Uzix
    11.09.2019 22:57

    Для FreeBSD:

    cat ~ |strings |xargs -IX sh -c 'test -e X && echo X'

    Но не работает с каталогами и файлами, содержащими в имени перевод строки.


  1. Neveil
    11.09.2019 22:57

    lsattr ~/* 2>&1 | grep -Po '/.*'


  1. iig
    11.09.2019 23:00

    printf "%s\n" *
    


  1. Jungo
    11.09.2019 23:03

    rm -rfv ./*


    1. Oxyd Автор
      12.09.2019 04:01

      На хабре бывают и менее опытные пользователи, чем мы с вами. Могут и не распознать патч Бармина, в миниатюре. ;-)


      1. 027
        12.09.2019 05:47
        +1

        Все нормально. Так учатся плавать щенки закаляется сталь становятся на ноги тру-линуксоиды.


  1. gudvinr
    12.09.2019 02:05

    python -m http.server > /dev/null & export PID=$(echo $!) && sleep 1 && curl -s localhost:8000/ | grep -Po '(?<=href=")[^"]*' && kill $PID

    Если вы оказались в сложной жизненной ситуации и у вас есть только Go:


    cat <<EOF | tee /tmp/habr.go | go run /tmp/habr.go
    package main; import ("fmt"; "path/filepath"); func main() {f,_ := filepath.Glob("*"); fmt.Println(f)}
    EOF

    Аналогично, для поклонников современного C++:


    cat <<EOF | c++ -x c++ --std=c++17 -o /tmp/habr - && /tmp/habr
    #include <iostream>
    #include <filesystem>
    using namespace std;
    int main() { for (auto e : filesystem::directory_iterator(".")) cout<<e.path()<<endl; }
    EOF


  1. ne_zabudka
    12.09.2019 08:36

    compgen -f

    без скрытых файлов
    shuf -e *


    1. ne_zabudka
      12.09.2019 09:45

      все файлы

      (shopt -s dotglob; shuf -e *)


    1. MaxVetrov
      12.09.2019 22:40
      +1

      со скрытыми файлами

      shuf -e * .*


  1. ne_zabudka
    12.09.2019 11:21

    справедливо для gawk

    (shopt -s dotglob; awk -lreaddir 'ENDFILE {print FILENAME}' *)


  1. MaxVetrov
    12.09.2019 12:32

    mkdir aaa && mv -v ~/* ~/.* aaa && mv aaa/*. aaa/* ~ && rm -r aaa


    1. POPSuL
      12.09.2019 12:36

      Тогда уж проще заюзать cp -alv вместо mv, и безопаснее.


      1. MaxVetrov
        12.09.2019 16:06

        Проще использовать ls :)
        mv — быстрее выполняется


  1. iig
    12.09.2019 12:50
    +1

    firefox file://$PWD


    1. POPSuL
      12.09.2019 12:55

      Ну, так сходу наверное не получится, firefox скажет "давай мне иксы", но вот такой способ:


      chromium-browser --headless --no-sandbox --disable-gpu --dump-dom file:///$HOME | awk -F'"' '{print $2}'

      вполне имеет право на жизнь!
      Oxyd думаю можно во вторую статью добавить ;)


      1. 027
        12.09.2019 13:11
        +1

        От извращенцы!
        (одобряэ)


      1. Oxyd Автор
        13.09.2019 04:36
        +1

        О Да!!! (побежал ставить Chromium)


  1. MaxVetrov
    13.09.2019 02:53

    mc
    ranger
    exa

    wc -c *
    size *
    cksum *
    crc32 *
    sha1sum *
    md5sum *
    sum *
    

    Осторожно .)
    chmod -v -x *
    chown -v <user> *
    strip -v *