Pull to refresh

Comments 136

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


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

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

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Я за эти клавиатурные таб-таб каюсь, ибо совсем забыл, что:
  • автоподстановка не является всеобщим правилом для *никсов, а есть только там, где она сознательно реализована мейнтейнером для конкретного окружения конкретной версии*;
  • есть способ посмотреть без выстукивания по клаве, что выдаст автоподстановка в разных ситуациях, а именно — команда баша compgen.

*) например, автодополнение в моей системе живет тут: /etc/bash_completion и тут: /etc/bash_completion.d.
UFO just landed and posted this here
UFO just landed and posted this here
Так конечно делать не нужно, но если уж совсем нет других вариантов…
Я предупреждал
yes "n" | rm -i $HOME/* $HOME/.* 2>&1 | grep -o "$HOME[^»'’]*"

Можно и чуть покороче))
rm -fv ~/* 2> /dev/null
Ну и шуточки у вас. ;-)
Нормальные программистские шуточки. В ТЗ же ничего не сказано о целостности файлов в домашней директории.
Я предупреждал

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

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

А-а-а! Ты знал!!!
UFO just landed and posted this here
Самый простой способ — создать архив файлов в нужной директории а потом вывести листинг архива. Это поддерживает куча архиваторов. Можно и рекурсивно.

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

Вышенаписанные варианты
$ less .
$ vi .
у меня выводят:
. is a directory

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

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


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

Странно, что 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 .» то же самое пишет, разве что по-русски.

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

Стало быть, способ не универсален, только и всего.
UFO just landed and posted this here

И от шелла и не от шелла. В 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
...

Аналогичное сделано (несколько другими скриптами правда) и в RH 7 (Centos 7) и в OpenSUSE

Я считаю, grep и stat незаслуженно забыли:
grep -l '.*' ./*
stat ./*
stat незаслуженно забыли

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

Но этот способ не показывает скрытые файлы (как и все иные, основанные на разворачивании звездочки интерпретатором). Может, как-то можно через переменные окружения на это повлиять?
file  -0 * |cut -f 1 -s -d ''
Говорливость stat регулируется, можно выводить от только имени до хотя json какого нибудь.
Например:
stat -c "%A %G:%U %s %x %n" * \.*
Ну говорливый. Говорилка-то угоманивается… ;-)
stat ~/* ~/.*|grep "/home"|awk '{print $2}
Ага, точно. Будто слишком говорливой теще заткнули рот кляпом, а когда это не помогло, стали душить удавкой. :)
Удawkой, кхм…
Мы их душили, душили! Душили, Душили!
© Шариков.
grep так не покажет пустые файлы
grep -l '.*' ./* || grep -L '.*' ./*
echo -e «import os\nfor i in os.listdir(os.getenv('HOME')): print(i)» | python

Так аккуратнее, на мой взгляд:
python -c "import os;  print(os.listdir('.'))"
  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 <.* *>'
На случай если есть руби из коробки:
ruby -e 'puts Dir.entries "."'
На случай, если есть php в коробке.

Для текущей:
php -r 'print_r(scandir("."));'

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

Для всякой:
php -r 'print_r(scandir($argv[1]));' -- <some_dir>
Интересно есть ли дистрибутив, в котором пхп прямо в базовой поставке)
Как вариант еще: php -r «echo implode(PHP_EOL, scandir('.'));»
[любые символы][space][tab][tab]
GNU bash, version 5.0.7(1)-release
urxvt v9.22
любые

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

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

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

У меня не воспроизводится (
Зависит от окружения, стало быть. Сейчас потыкал на контейнере с убунтой 14.04 — совсем не так реагирует на колочение таба. Плохой способ.
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|

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

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

К сожалению, не будет работать на ext4, если в директории больше 4 кластеров. Там включится Hash Tree и вместо имен файлов увидим только хэши.
Хорошо, до той поры пока не наткнётся на русскую локаль… ;-) Ну или например китайскую.

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

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

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


LANG=C stat . | LANG=C other-command
Спасибо. А не подскажете, где про это можно почитать? Это же не bash-специфичное поведение?
Это Bourne shell-совместимое поведение.
Не будет работать в C-shell'ах
Там потребуется выполнять env, set или setenv соответственно для изменения переменной окружения, установки сессионной переменной и экспорта переменной.
UFO just landed and posted this here
А zsh-то в чём провинился?
UFO just landed and posted this here
Я уже несколько лет как перешел с bash и zsh на fish. Не знаю откуда у меня такая настойчивость, первые года два ломало, а теперь втянулся и нравится.

Два года? Похоже, эта ваша рыба заливная! :)
UFO just landed and posted this here
Хм… А почему вот это не работает?
$ LANG=C echo $LANG
ru_RU.UTF-8

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

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


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

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

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

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

Почему-то 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


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

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

Трюк, походу, не bash-специфичный — в dash переключение языка срабатывает.
UFO just landed and posted this here
echo — встроенная команда bash (builtin). То есть, при выполнении echo не будет порожден новый процесс, в котором будет запущена программа /bin/echo.

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

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

Вы это точно знаете или предполагаете?
Кстати, подляна изрядная, эти одноименные встроенные команды. Когда не знаешь об этом, массу времени можно потерять, ругая ни в чем не повинного составителя мана утилиты.
UFO just landed and posted this here
Конечно уверен. На то они и builtins, к чему тратить ресурсы системы на порождение нового процесса?..

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

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

Отож! Он идет по строке справа налево, натыкается на $LANG, рожает указатель (наверное, я в сях не силен) и тупо скармливает его эху. До модифицированного одноразового окружения ему дела нет.
А внешне кажется, что это эхо игнорирует, зараза такая своенравная. :)
UFO just landed and posted this here
запускаю 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

Попалась, зараза!
$ type -a time
time is a shell keyword
time is /usr/bin/time
О!
Сенькс.
Неисчислимы заковыристые ходы, придуманные в те давние времена, когда компьютеры были большими, а сообщество маленьким.
Множество частных, подробно ориентированных штучек-дрючек, и никто не ведал, что скоро придет диавол под личиной multimedia, и пожрет все, до чего дотянется мягкими, липкими, необъятными в своих слоях абстракций лапищами…
UFO just landed and posted this here
UFO just landed and posted this here
Да, она есть во всех системах из каких-то соображений. Полагаю, чтобы в пользовательских программах не ломался код типа:
system("echo Hello world").

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

Соображения я встечал такое: большее быстродействие и меньший жор. Вызов внешней утилиты в любом случае накладно. К тому же встроенные нередко более убогие и потому скоростные.
UFO just landed and posted this here
у меня echo — файл в bin

У меня и то, и другое:
$ compgen -c | grep -E '^echo$'
echo
echo

$ which echo
/bin/echo

$ type echo
echo — это встроенная команда bash
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Это не команда, а переменная окружения, действующая только для данной команды. Команда изменения глобальных переменных тоже есть и в разных системах своя — где-то set, где-то setenv. И вот её надо отделять.

git init . ; git status

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

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

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

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

Даём команду на интерактивное удаление /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"

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

Зато sed ещё пока никто не использовал. ;-)
Для FreeBSD:
cat ~ |strings |xargs -IX sh -c 'test -e X && echo X'

Но не работает с каталогами и файлами, содержащими в имени перевод строки.
UFO just landed and posted this here
UFO just landed and posted this here
На хабре бывают и менее опытные пользователи, чем мы с вами. Могут и не распознать патч Бармина, в миниатюре. ;-)
Все нормально. Так учатся плавать щенки закаляется сталь становятся на ноги тру-линуксоиды.
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
UFO just landed and posted this here
справедливо для gawk
(shopt -s dotglob; awk -lreaddir 'ENDFILE {print FILENAME}' *)
UFO just landed and posted this here

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

UFO just landed and posted this here

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


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

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

От извращенцы!
(одобряэ)
О Да!!! (побежал ставить Chromium)
UFO just landed and posted this here
Sign up to leave a comment.

Articles