Comments 136
cd ~; vi .
(zsh)
~/ [tab][tab]
vim .
mv ~/ <Tab><Tab>
dd if=~/ <Tab><Tab>
и т.п.
- автоподстановка не является всеобщим правилом для *никсов, а есть только там, где она сознательно реализована мейнтейнером для конкретного окружения конкретной версии*;
- есть способ посмотреть без выстукивания по клаве, что выдаст автоподстановка в разных ситуациях, а именно — команда баша
compgen
.
*) например, автодополнение в моей системе живет тут:
/etc/bash_completion
и тут: /etc/bash_completion.d
.yes "n" | rm -i $HOME/* $HOME/.* 2>&1 | grep -o "$HOME[^»'’]*"
rm -fv ~/* 2> /dev/null
Я предупреждал
Но мы же все делаем бэкап $HOME, правда?
grep "" -lr .
less .
$ 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 нет. :)
И от шелла и не от шелла. В 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
rsync --list-only ~/
grep -l '.*' ./*
stat ./*
stat незаслуженно забыли
stat дает чрезмерно говорливый выхлоп, а в задаче надо просто список файлов.
Тогда уж лучше file:
file ~/*
Но этот способ не показывает скрытые файлы (как и все иные, основанные на разворачивании звездочки интерпретатором). Может, как-то можно через переменные окружения на это повлиять?
grep -l '.*' ./* || grep -L '.*' ./*
gzip -cv1 ./* | gzip -l
head -n 0 -v ./*
echo -e «import os\nfor i in os.listdir(os.getenv('HOME')): print(i)» | python
Так аккуратнее, на мой взгляд:
python -c "import os; print(os.listdir('.'))"
- echo и print
Эти команды не идентичны. Возможно Вы имели в виду printf, но она может сбоить в некоторых случаях, хотя echo — тоже.
- tar
У вас мешанина в примере — /dev/null и null.
- 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 -r 'print_r(scandir("."));'
Для домашней:
php -r 'print_r(scandir($argv[1]));' -- ~
Для всякой:
php -r 'print_r(scandir($argv[1]));' -- <some_dir>
GNU bash, version 5.0.7(1)-release
urxvt v9.22
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|
:)
stat *|awk '/File:/{print $2}'
LANG=C stat ...
никто не отменял :)
Кто объяснит, почему в подобных конструкциях после задания переменной не нужно ставить точку с запятой? Это же отдельная команда, по идее.
Нет, это изменение environment variable для запускаемой программы, а не глобально. именно поэтому при "пайпинге" нужно указывать для каждой команды свои переменные окружения (ну, если нужно конечно), вроде
LANG=C stat . | LANG=C other-command
Это работает даже в sh
.
Почитать об этом можно в разделе https://help.ubuntu.com/community/EnvironmentVariables#Bash.27s_quick_assignment_and_inheritance_trick
Ну и вообще, вся статья полезна.
Не будет работать в C-shell'ах
Там потребуется выполнять env, set или setenv соответственно для изменения переменной окружения, установки сессионной переменной и экспорта переменной.
$ 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 переключение языка срабатывает.
echo — встроенная команда bash (builtin). То есть, при выполнении echo не будет порожден новый процесс, в котором будет запущена программа /bin/echo.
Я приводил в исходном каменте оба варианта, со встроенным эхом и с
/bin/echo
. Поведение одинаковое.встроенная команда bash… не будет порожден новый процесс
Вы это точно знаете или предполагаете?
Конечно уверен. На то они и builtins, к чему тратить ресурсы системы на порождение нового процесса?..
Уверенность на основе рассуждения есть предположение. :) Ну да бог с ним. Дело тут совсем не в отдельности процесса.
Ах, да, прав комментатор про то, что bash подставляет значения переменных при вызове. Конечно, когда мы пишем cmd $VAR любой shell обязан подставить $VAR до вызова команды.
Отож! Он идет по строке справа налево, натыкается на $LANG, рожает указатель (наверное, я в сях не силен) и тупо скармливает его эху. До модифицированного одноразового окружения ему дела нет.
А внешне кажется, что это эхо игнорирует, зараза такая своенравная. :)
запускаю 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, и пожрет все, до чего дотянется мягкими, липкими, необъятными в своих слоях абстракций лапищами…
Да, она есть во всех системах из каких-то соображений. Полагаю, чтобы в пользовательских программах не ломался код типа:
system("echo Hello world")
.
Если в путях есть
/bin
ничего не сломается. А так должно быть обязательно. Ну, если это не наколенная эмбедовка от васяна, понятно.Соображения я встечал такое: большее быстродействие и меньший жор. Вызов внешней утилиты в любом случае накладно. К тому же встроенные нередко более убогие и потому скоростные.
у меня echo — файл в bin
У меня и то, и другое:
$ compgen -c | grep -E '^echo$'
echo
echo
$ which echo
/bin/echo
$ type echo
echo — это встроенная команда bash
Это не команда, а переменная окружения, действующая только для данной команды. Команда изменения глобальных переменных тоже есть и в разных системах своя — где-то set, где-то setenv. И вот её надо отделять.
git init . ; git status
Будет немного лишнего, но можно отфильтровать.
Очень простая, но реальная задача:
веб-камера с записью "сошла с ума" и вместо записи по движению скинула на флэшку непрерывную запись целой недели. ~20 тыс. файлов. ФС fat32.
ls
(просто) в папке DCIM ничего не выводит (думает минут 5, потом убивается системой).
И вот тут выясняется, что ls написан внутри очень неплохо! Он не получает "список всего", а потом накладывает фильтр, а сразу фильтрует при выводе на экран. В итоге ls "20190901*" и т.д. вполне сработали! (последующий rm так же сработал по тому же паттерну)
Только это не ls и rm такие крутые, а шелл, который развернул имя со звездочкой в список файлов и уже их передал списком аргументов (и тут кстати можно словить ошибку, если файлов больше, чем максимально разрешенное число аргументов командной строки)
Как удалить миллионы файлов из одной папки
Так как же удалить миллионы файлов из одной папки
Даём команду на интерактивное удаление /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"
hdfs dfs -ls
без использования ls
«Ты видишь суслика? И я не вижу.»
$'\x6c\x73' $'\x2d\x61'
cat ~ |strings |xargs -IX sh -c 'test -e X && echo X'
Но не работает с каталогами и файлами, содержащими в имени перевод строки.
lsattr ~/* 2>&1 | grep -Po '/.*'
printf "%s\n" *
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
compgen -f
без скрытых файлов
shuf -e *
(shopt -s dotglob; awk -lreaddir 'ENDFILE {print FILENAME}' *)
firefox file://$PWD
Нетипичный «ls» или как развлекаются линуксоиды