являются сегментами IF-FI, необходимые при итерации в списке каталога Pushd-POPD?

Как насчет использования «expr» или «rev»?

Ответ, аналогичный тому, который дал @ G-Man : expr "$ yourstring": '. * \ (. .. \) $ ' У него тот же недостаток, что и у решения grep.

Хорошо известный трюк - комбинировать «вырезать» с «оборотом»: echo «$ yourstring» | rev | cut -n 1-3 | rev

-1
06.05.2018, 18:19
5 ответов

Что произойдет, если pushdвыйдет из строя? Если команды должны выполняться в этих каталогах, очевидно, что перед выполнением команд следует проверить, успешно ли pushdвыполнено.

1
28.01.2020, 05:06

В дополнение к тому, что написал @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
6
28.01.2020, 05:06

Использование 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/
1
28.01.2020, 05:06

Если вы работаете с глобусом */, оболочка может выполнить проверку разрешений за вас, если вместо этого вы используете */..

$ mkdir x y z
$ chmod -x y
$ echo */
x/ y/ z/
$ echo */.
x/. z/.

Таким образом, pushdснизит вероятность отказа. Однако это также можно считать плохим стилем, поскольку этот код на самом деле не дает понять, что */.имеет такое намерение. С ifнамерение очевидно :убедитесь, что оно удастся, иначе.

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

Однако, к сожалению, также нормально не проверять каждую команду на наличие ошибок, а просто надеяться/верить, что большую часть времени все будет работать как задумано. Многие сценарии оболочки предназначены только для простых помощников и быстрых взломов.

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

popdможет не работать и в некоторых (неясных )случаях. Кто-то переименовал каталог? Очень плохо. Никто не проверяет эти вещи, потому что это того не стоит. пожимает плечами

Если вы не изменяете какие-либо переменные внутри этого вашего цикла, вы можете создавать каталоги для своих переключателей в подоболочках, а не в pushd/popd. Каждая подоболочка имеет свой собственный рабочий каталог, в то время как родительская оболочка сохраняет свой. Но если вы глубоко рекурсируете, это даст вам ад подпроцесса вместо стека подкаталогов, за которыми нужно следить.

Иногда возможна работа с абсолютными путями.

1
28.01.2020, 05:06

С помощью gnu -find можно сделать:

find ${drt} -maxdepth 1 -type d -execdir command1 ";" -execdir command2 ";"

Обратите внимание, что не каждая реализация find имеет параметр -execdir.

1
28.01.2020, 05:06

Теги

Похожие вопросы