Как насчет использования «expr» или «rev»?
Ответ, аналогичный тому, который дал @ G-Man : expr "$ yourstring": '. * \ (. .. \) $ '
У него тот же недостаток, что и у решения grep.
Хорошо известный трюк - комбинировать «вырезать» с «оборотом»: echo «$ yourstring» | rev | cut -n 1-3 | rev
Что произойдет, если pushd
выйдет из строя? Если команды должны выполняться в этих каталогах, очевидно, что перед выполнением команд следует проверить, успешно ли pushd
выполнено.
В дополнение к тому, что написал @Olorin , я думаю, здесь может быть пара недоразумений. Во-первых, for y in ${x}/*; do pushd "$y"; then
приводит к
bash: syntax error near unexpected token `then'
Во-вторых, отступ может ввести вас в заблуждение относительно того, что происходит на самом деле. Возьмите эту правильно отформатированную версию исходного кода:
for y in ${x}/*/
do
if pushd "$y"
then
command1
command2
popd
fi
done
Другими словами, все command1
, command2
и popd
запускаются только в том случае, если первоначальный pushd
завершился успешно. Если вы вместо этого написали
for y in ${x}/*/
do
pushd "$y"
command1
command2
popd
done
и не было errexit
охранника, сбой pushd
или popd
не повлияет на остальную часть скрипта. Это может привести к запуску command1
и command2
в неправильном каталоге, а затем, возможно, возврату в другой каталог , не связанный с этим кодом. Это могло иметь катастрофические последствия.
Наконец, я бы сказал, чтоpushd
+ команды + popd
является анти--паттерном , потому что он добавляет больше контекста (и, следовательно, когнитивных накладных расходов и риска )к языку, где сложный контекст это уже массовая проблема. Наиболее распространенный способ обойти это — передать путь (, идеально абсолютный ), командам, например:
for y in "$x"/*/
do
command1 "$y"
command2 "$y"
done
Использование if является разумной защитой от неудачного изменения каталога.
Этот код будет выполнять команды в родительском каталоге, если каталог не является исполняемым (нет разрешения x ).
#!/bin/bash
drt="/var/www/html"
for dir in "${drt}"/*/
do pushd "$dir"
pwd
popd
done
Создайте пару каталогов, измените владельца и разрешения:
$ mkdir -p /var/www/html/{one,two}
$ sudo chown user:user /var/www/html/{one,two}
$ sudo chmod o-x /var/www/html/two
$./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp
./script: line 5: pushd:./var/www/html/two/: Permission denied
~/temp
./script: line 7: popd: directory stack empty
Команда pushd выдала ошибку, но команда pwd была выполнена в каталоге ~/temp (обратите внимание на значение ~/temp, напечатанное сразу после ошибки ). Это явный риск сделать что-то не так. Сравните с этим скриптом:
#!/bin/bash
drt="./var/www/html"
for dir in "${drt}"/*/
do if pushd "$dir" 2>/dev/null
then
pwd
popd
fi
done
Выполнен новый скрипт:
$./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp
Или даже лучше:
#!/bin/bash
drt="./var/www/html"
for dir in "${drt}"/*/
do if pushd "$dir" 2>/dev/null
then
pwd
popd
else
echo "Failed to change to dir=$dir" >&2
exit 7
fi
done
Который при выполнении выведет:
$./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp
Failed to change to dir=/var/www/html/two/
Если вы работаете с глобусом */
, оболочка может выполнить проверку разрешений за вас, если вместо этого вы используете */.
.
$ mkdir x y z
$ chmod -x y
$ echo */
x/ y/ z/
$ echo */.
x/. z/.
Таким образом, pushd
снизит вероятность отказа. Однако это также можно считать плохим стилем, поскольку этот код на самом деле не дает понять, что */.
имеет такое намерение. С if
намерение очевидно :убедитесь, что оно удастся, иначе.
В целом было бы правильным всегда проверять каждую команду на наличие ошибок. Так что делайте это, если это можно сделать легко и не слишком запутывая ваш код.
Однако, к сожалению, также нормально не проверять каждую команду на наличие ошибок, а просто надеяться/верить, что большую часть времени все будет работать как задумано. Многие сценарии оболочки предназначены только для простых помощников и быстрых взломов.
Добавление проверки для каждой возможности И написание кода для разумной обработки каждой возможной ошибки быстро станет запутанным и нечитаемым. Читабельность кода тоже важна.
popd
может не работать и в некоторых (неясных )случаях. Кто-то переименовал каталог? Очень плохо. Никто не проверяет эти вещи, потому что это того не стоит. пожимает плечами
Если вы не изменяете какие-либо переменные внутри этого вашего цикла, вы можете создавать каталоги для своих переключателей в подоболочках, а не в pushd/popd. Каждая подоболочка имеет свой собственный рабочий каталог, в то время как родительская оболочка сохраняет свой. Но если вы глубоко рекурсируете, это даст вам ад подпроцесса вместо стека подкаталогов, за которыми нужно следить.
Иногда возможна работа с абсолютными путями.
С помощью gnu -find можно сделать:
find ${drt} -maxdepth 1 -type d -execdir command1 ";" -execdir command2 ";"
Обратите внимание, что не каждая реализация find имеет параметр -execdir.