Pull to refresh

Comments 21

Исправьте, пожалуйста, кавычки в теле статьи.

[ -z «$var» ] “foo bar” - это все неправильно. Вместо 4 этих символов в статье должен быть символ " и только. Типографские улучшения тут все испортили.

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

Ога, а ещё часто бывает

v="new folder"
rm -f $v

А потом оказывается, что папки new нет, и старые бэкапы годами не удаляются. Нормальная ситуация в баше.

Это ненормальная ситуация в баше.
Нормальная ситуация в баше больше похожа на это:
v="new folder"
rm -f "$v"

Если вы не заботитесь об экранировании/кавычках, это не проблема баша, он просто позволяет вам сделать то, что вы (предположительно) хотите. Есть кейсы, когда такое использование имеет смысл. Да и традиционно не принято в линуксе имена с пробелами использовать.
Или я не понял сарказма.

Такое использование имеет смысл только в одном случае — при намеренной записи в переменную нескольких аргументов. Вот только когда один из этих аргументов с пробелом — проблемы начинаются снова.


Поэтому для передачи нескольких аргументов правильно использовать массивы, а для передачи одного — кавычки. А прямое использование $v лучше считать устаревшим. И тот факт, что самая простая запись является устаревшей — как раз таки проблема баша.

при намеренной записи в переменную нескольких аргументов

Именно! В этом случае это как раз то, что ожидаешь, и обычно понимаешь (или думаешь, что понимаешь), как оно должно работать.
А прямое использование $v лучше считать устаревшим.

Не факт. Например:
declare -i v
for $v in {1..10}
...

Проблема баша не в этом, я думаю. Есть явно описанные в документации вещи, которые считаются устаревшими (наподобие `команда` и $(команда)). Проблема баша скорее в том, что он вас не ограничивает сам по себе, на нем легко писать плохо и трудно писать хорошо. И при всей внешней простоте уровень входа выше, чем кажется. Потому что надо помнить, что нельзя ставить пробел перед знаком «равно» или после него, но не всегда, и так далее. Или помнить про передачу параметров (кавычки, $@, $*, IFS).

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

Именно так и есть.


Для сравнения, в powershell передача $x всегда означает передачу одного параметра, а для передачи нескольких существуют массивы и @x.


Или взять шелл Inferno OS — там любая переменная является списком строк, и при подстановке через $x этот список неизбежно "раскроется" — вот только при этом каждая строка в списке может содержать пробелы.

v=«new folder»
rm -f $v


А такая ситуация вас не удивляет:
$ myvar="one two three four five"

$ for item in $myvar; do echo $item; done
one
two
three
four
five

$ for item in "$myvar"; do echo $item; done
one two three four five

Может вы просто не понимаете, что в bash типы переменных это не объекты питона, и строка, число и массив взаимозаменяемы. Поэтому кавычки это не где-то там, а первое, про что нужно почитать, и правильно ими пользоваться. Ну а то, что часто бывает — правило 90%.

А такая ситуация вас не удивляет:

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

Может вы просто не понимаете, что в bash типы переменных это не объекты питона

Я где-то писал возмущение по поводу кавычек? Или к чему это? Если вы про "нормальную" ситуацию, то, хорошо, давайте назовём это регулярной ситуацией, когда приходишь на новое место и правишь все скрипты, которые если и работали, то только потому что имена без пробелов и спец символов выбирали.

У меня к башу особых претензий нет. Я уже давно смирился что его мама таким родила

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

У меня к башу особых претензий нет. Я уже давно смирился что его мама таким родила


Это элементарное непонимание базовых вещей, которые описаны в документации. Как только их осиливаешь, bash становится прекрасным.
Вот честно, у меня к башу намного меньше претензий, чем к js или python
Аналогично. База не такая большая. Я в своё время пытался начать писать на питоне, заняло 3 года, потому что на баше можно было сделать короче и без дополнительных библиотек и т.д.
Как здорово, что в этой теме нашлись гуру баша.

ну а как вы это объясните?

server@user$ BLOB=blahblah /usr/bin/python3 -c "from os import environ; print(environ.get('BLOB', 'Not found'))"
blahblah
server@user$ BLOB=blahblah /bin/echo "${BLOB}"

server@user$ BLOB=blahblah /bin/echo "$BLOB"

server@user$ BLOB=blahblah /bin/echo $BLOB

server@user$ export BLOB=blahblah; /bin/echo $BLOB
blahblah


Даже проверил ваши выкладки сообщением ниже, на всякий случай:


server@user$ type echo
echo is a shell builtin
server@user$ type /bin/echo
/bin/echo is /bin/echo


И тут, конечно же, работает в соответствии с вашими ожиданиями, строго по man'у:

server@user$ INTERNAL_BLAH="BLAH=blahblah"; export $(echo $INTERNAL_BLAH); echo $BLAH
blahblah
server@user$ INTERNAL_BLAH="BLAH=blah blah"; export $(echo $INTERNAL_BLAH); echo $BLAH
blah
server@user$ INTERNAL_BLAH='BLAH="blah blah"'; export $(echo $INTERNAL_BLAH); echo $BLAH
-bash: export: `blah"': not a valid identifier
"blah


Или опять ошибка пользователей в том, что они после каждого неадекватного/нестандартизированного поведения в man не лезут (причём перед тем как это поведение возникнет)?
Смотрим сюда: github.com/coreutils/coreutils/blob/master/src/echo.c
Видим, что команда echo делает. Вспоминаем, что раскрытие переменных происходит до отправки строки на выполнение и что в текущем окружении переменная BLAH не определена.
В питоне вы сначала наследуете окружение, а потом используете внутри программы (читаете из него). Это равнозначно «BLAH=blah; /bin/echo $BLAH».
При использовании «BLAH=blah /bin/echo $BLAH» на выполнение уйдет «BLAH=blah echo», но при этом в окружении, в котором echo выполняется, переменная окружения есть.
man bash, «SIMPLE COMMAND EXPANSION»
А причем тут баш?
Это вообще архитектура операционной системы и банальная логика.

Это все равно что в питоне сделать какой-то:
python -c 'import os;myvar=123;os.system(«echo $myvar»)'
Вы же в питоне не ожидаете, что локальная переменная myvar вдруг будет доступна внешнему дочернему процессу?

Переменную нужно экспортировать в environment переменные, тогда все будет ок.
Читайте про команды env и export и пользуйтесь ими правильно:
$ export BLOB=blabla
$ python -c "from os import environ; print environ.get('BLOB')"
blabla

$ BLOB=blahblah;echo "${BLOB}"

$ env BLOB=blahblah /bin/echo "$BLOB"

$ export BLOB=blahblah; /bin/echo $BLOB
blahblah


Или опять ошибка пользователей в том, что они после каждого неадекватного/нестандартизированного поведения в man не лезут

Ну вы привели то, что даже не в man надо читать, а вообще как-то подумать, что вы хотите данные перекидывать между РАЗНЫМИ процессами. И bash тут как бы второстепенен.

P.S. Ну и да, есть еще ресолв переменных. Если вы пишете однострочник в баш, то перед тем как выполниться, вся строка парсится и переменные приводятся к их значениям, а уже потом выполняется. Поэтому следующее сработает:
var=blabla;/bin/echo $var
blabla


можете включить отладку (set -x) — перед выполнением, баш будет показывать команду целиком, уже с уже evaluated значениями:
$ set -x
$ var=blabla;/bin/echo $var
+ var=blabla
+ /bin/echo blabla
blabla
В том сабреддите хорошо смотрелась бы команда [ -z $var ].


Ну вот почему?
Ну любой, кто хоть немного начинает учить bash, сразу знает о разнице между [] и [[ ]], и что в случае [ ] нужно подумать про кавычки, чтобы не нарушить синтаксис.

$ [ -n "$var" ] && echo true || echo false
false

Все корректно, переменная не задана — получаем false

$ var=something
$ [ -n "$var" ] && echo true || echo false
true

Переменная задана — получаем true

Какова мораль сей басни? Она такая же, как и всегда: не забывайте о кавычках.

Да нет, просто если пишешь что-то на языке, изучи его самые-самые базовые основы.
Я эти моменты даю на своей первой вводной лекции к shell

Если пишешь на bash и для bash — используй [[ ]]
Если пишешь posix-совместимо, почитай справку про [] и пиши правильно.
Ну вот почему?

Потому что люди не читают man bash?
Ну, тут есть всякие интересные моменты.

Когда я пытаюсь смотреть внутрь я вообще не вижу попытки обращения к '[' (программе). Возможно, дело в современном баше или я пользуюсь strace-ом неправильно, но

╭─sirex at sirex-notebook in /tmp 21-06-01 - 14:12:35
╰○ cat test.sh
#!/bin/bash

if [ -n "FOO" ]; then echo FOO; fi
/usr/bin/\[ -n FOO \] && echo ok
╭─sirex at sirex-notebook in /tmp 21-06-01 - 14:12:39
╰○ strace bash test.sh 2>&1 | grep stat | grep /bin
stat("/home/sirex/.cargo/bin/bash", 0x7ffd9304a900) = -1 ENOENT (No such file or directory)
stat("/home/sirex/.local/bin/bash", 0x7ffd9304a900) = -1 ENOENT (No such file or directory)
stat("/home/sirex/.cargo/bin/bash", 0x7ffd9304a900) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/bash", 0x7ffd9304a900) = -1 ENOENT (No such file or directory)
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1234376, ...}) = 0
╭─sirex at sirex-notebook in /tmp 21-06-01 - 14:13:00
╰○ bash --version
GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)


Может, Баш неявно и услужливо преобразовывает вызов [ в нативный для себя вариант…
Это, конечно, не отменяет того, что лучше прочитать мануал и сделать наверняка правильно.
Не может, а так и есть. В баше "[" — это внутренняя команда.
по умолчанию [ это внутренняя команда.
Собственно есть команда type, которая вам и расскажет что в текущем енвайрнменте будет использоваться. И нельзя type путать с which — which это внешняя, она про внутренние команды, алиасы и функции — ничего не знает, а все они имеют приоритет над тем что в PATH

Пример:
$ which [
/usr/bin/[

$ type [
[ is a shell builtin

$ type -P [
/usr/bin/[
Да, понимаю, но вторая команда у меня тоже не вызвала в итоге '[', хотя я явно указал её.
Ну этого уже не может быть…
можете попробовать в скрипте сделать
[ --help
/usr/bin/[ --help

в первом случае будет синтаксическая ошибка
Sign up to leave a comment.