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
ну а как вы это объясните?
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 не лезут (причём перед тем как это поведение возникнет)?
Видим, что команда 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-совместимо, почитай справку про [] и пиши правильно.
Когда я пытаюсь смотреть внутрь я вообще не вижу попытки обращения к '[' (программе). Возможно, дело в современном баше или я пользуюсь 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/[
О неоправданно хорошей работе [ -z $var ]