Комментарии 35
О-о-ох, а Вы всё продолжаете писать ужасный код, и ладно бы сами писали для себя скрипты, так нет, это статья на весь рунет!
Прочтите для начала Bash Pitfalls, а то я не знаю с чего начать комментирование Вашей статьи…
Это перевод, конечно, но из той серии, которую переводить лучше не стоит. Автор вообще сам себе противоречит, сначала пишет, что в названиях файлов/директорий могут быть пробелы, а потом что-то типа:
for file in $folder/*
Согласен. Не стоит учиться на таких переводах.

С пробелами в данном случае прокатит. А вот например если имя файла содержит *, то попробует раскрыть как вайлдкард.

С пробелами в данном случае прокатит

$ ls 
folder 1  folder 2
$ for folder in $(ls); do for file in $folder/*; do echo $file; done; done
folder/*
1/*
folder/*
2/*

Если вывод ls парсить, то не прокатит. А если напрямую раскрывать for file in folder/*, то вполне:


$ ls -1
a b
c d
$ for i in ./*; do echo $i; done
./a b
./c d
Давайте поясню. В статье $folder — переменная. Каким образом ей присвоили значение — совершенно неважно, важно то, что в примере она используется с ошибкой. Я тупо взял пример из статьи, без всяких «если» :)

Запутался, про пробелы в каком месте речь :) Да, если в $folder пробел, тоже сломается.

find /path -print0 |\
  while IFS= read -r -d $'\0' path; do
    #спасает от... от чего только не спасает, проверить можно просто:
    echo -e "${path}"
  done

While там для того, чтобы можно было с переменной path оперировать дальше.

С read -d $'\0' трюк не знал, спасибо :)


Тут кстати ещё одна проблема возникает — while в таков варианте запустит сабшел, изменения переменных в котором не видны в родительском. Поэтому обмен данными с командами, которые внутри while работают, может быть проблематичен.

while в таков варианте запустит сабшел

Это скорее сила привычки и того что «внутренности» while в моём конкретном случае не были нужны. «Исправляется» очень просто:
while IFS= read -r -d $'\0' path; do
  echo -e "${path}"
done < <(find /path -print0)
find | xargs же.
Зачем писать мутные 3 строчки, если можно в одну?

Если одной команды после xargs не достаточно, а нужна сложная обработка, с условиями и переменными.

Perl же. Там есть и чтение чужого выхлопа в массив, и анализ файла на вхождение строки, и достаточно по человеческих выглядит, и не нужно бояться, что в имени файла попадется не тот символ.
Так я надеюсь понятнее (вместе с объяснениями Павла)?
#!/usr/bin/env bash

TOPVAR="this is topvar"

find ~ -maxdepth 1 -type d -print0 |\
    while IFS= read -r -d $'\0' path; do
        TOPVAR="this is done in subshell"
    done

echo -e "subshell version"
#prints - "this is topvar", because pipe creates subshell
echo -e "${TOPVAR}"

#----------------
echo -e ""
# let's do it again without subshell
while IFS= read -r -d $'\0' path; do
    TOPVAR="this is not done in the subshell, result is visible"
done < <(find ~ -maxdepth 1 -type d -print0)

echo -e "non-subshell version"
# prints - this is not done in the subshell, result is visible
echo -e "${TOPVAR}"


selivanov_pavel
Не вводите пож. людей в заблуждение фразой
while loop creates separate subshell

Сабшел создаёт пайпа.
Пора запретить статьи о bash на хабре конвенцией ООН.
Только за эту неделю психика линуксоидов подверглась геноциду раз, два, три (эта публикация) раза
Статью нужно назвать иначе, что-то в стиле «Основы программирования на примере bash». Излагается все для тех, кто никогда про циклы не слышал.
Предлагаю также примеры брать более жизненные. Например, тот же анализ passwd — но чтобы результат был осмысленным. Зачем может пригодиться такой вот дамп форматированного файла?
КДПВ отлично иллюстрирует автоцензуру того, что я подумал при прочтении.
Сколько можно снова и снова плодить по кусочкам разбитые (и не полные) гайды по написанию скриптов на баше?
Есть замечательный документ — http://tldp.org/LDP/abs/html/, кто владеет английским — просто читайте его.
Авторам из ruvds предлагаю помогать перевести вышеупомянутый источник на русский, раз уж так руки чешутся. Пользы будет в разы больше.
Здравствуйте, спасибо за комментарий. В первой статье мы спрашивали нужно ли переводить остальные части, по результатам было принято решение переводить дальше
Результаты голосования
image
Окей, переводите дальше.

Но можете хотя бы оформлять код так, чтобы оно соответствовало bash style, отделять вложенные блоки отступом, именовать переменные в UPPERCASE?

#!/bin/bash
for VAR1 in 1 2 3 4 5 6 7 8 9 10
do
  if [ $VAR1 -eq 5 ]
  then
    break
  fi
  echo "Number: $VAR1"
done

Стиль и отступы мелочь по сравнению вот с этим из первой статьи:
Нас интересует bash, поэтому первая строка файла будет такой:
#!/bin/bash

Причём на хабре это уже как минимум один раз обсуждалось вот тут (советую пойти по ссылкам вглубь): https://habrahabr.ru/post/319670/#comment_10017494

Если лень искать и ходить по ссылкам, вот хорошо написано:
http://stackoverflow.com/questions/10376206/what-is-the-preferred-bash-shebang

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

Не надо парсить /etc/passwd, надо использовать getent. Иначе потом эти скрипты не будут работать на хостах, где пользователи прилетают из какого-нибудь LDAP

Если скрипт должен что-то искать именно в локальной базе — почему бы и нет.

Больше рассказывайте "почему" вы делаете так, а не иначе. Даже простое объяснение разницы между


if [ условие ]; then
#
fi

и


if [[ условие ]] 
then
   #
fi

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

По поводу разделителей.
Чем менять переменную IFS, которая повлияет и на последующую работу (т.е. надо не забыть восстановить), и на вложенные вызовы (как изолировать?), — лучше использовать транслятор символов.

echo $PATH

/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/mingw64/bin:/usr/bin:.......

echo $PATH | tr ':' '\n'

/mingw64/bin
/usr/local/bin
/usr/bin
/bin
/mingw64/bin
/usr/bin
.....

for d in `echo $PATH | tr : '\n'`; do echo "--- $d ---"; done

--- /mingw64/bin ---
--- /usr/local/bin ---
--- /usr/bin ---
--- /bin ---
--- /mingw64/bin ---
--- /usr/bin ---
.....


мне нужно прочитать список компов из текстового файла, по одному названию в строке
потом попинговать их по одному, если комп включен то его имя записываем в один файл
если комп выключен, то его имя записываем в другой файл
в cmd bat я это сделал легко
но в линуксе под Win10 со всеми последними обновлениями это сделать не получается
пишет что то про DO и все. пробовал все примеры из этой и других статей
ошибка может отличаться но не работает
ошибка при чтении списка файлов.

#!/bin/bash
for iplist1 in bank_router_ip.txt
do
echo "$iplist1"
done

lin@W10:/mnt/c/Users/IEU$ sh test
: not foundst:
: not foundst:
: not foundst:
: not foundest:
: not foundest:
: not foundest:
test: 36: test: Syntax error: end of file unexpected (expecting «done»)

#!/bin/bash
cat bank_router_ip.txt | while read p; do
echo $p
done

lin@W10:/mnt/c/Users/IEU$ sh test
: not foundst:
: not foundst:
: not foundst:
: not foundest:
: not foundest:
test: 14: test: Syntax error: «done» unexpected (expecting «do»)

#!/bin/bash
for planet in Меркурий Венера Земля Марс Юпитер Сатурн Уран Нептун Плутон
do
echo $planet
done

lin@W10:/mnt/c/Users/IEU$ sh test
: not foundst:
test: 4: test: Syntax error: word unexpected (expecting «do»)

что я не так делаю?
1. Шебанг #!/bin/bash, а вы его запускаете через sh (другой шелл)
2. test — встроенная команда, не называйте так скрипты

bash-4.4$ cat planets.bash
#!/bin/bash
for planet in Меркурий Венера Земля Марс Юпитер Сатурн Уран Нептун Плутон
do
echo $planet
done
bash-4.4$ bash planets.bash
Меркурий
Венера
Земля
Марс
Юпитер
Сатурн
Уран
Нептун
Плутон
lin@W10:/mnt/c/Users/IEU$ bash ttest
ttest: line 2: $'\r': command not found
ttest: line 4: syntax error near unexpected token `$'do\r''
'test: line 4: `do

Скорее всего, вы редактировали скрипт notepad или подобным редактором. Используйте нормальный редактор и unix-style line endings (LF, а не CRLF).

Возможно, что-то сейчас изменилось, но когда-то notepad++ был неплох. Или vscode от ms, говорят что там не ужасно.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.