Bash избегает тильды и подстановочных знаков, но не пространства - PullRequest
5 голосов
/ 18 января 2011

(РЕДАКТИРОВАТЬ в решении с помощью sed)

У меня есть список имен файлов и каталогов, включая тильду и подстановочные знаки.Например:

~/docs/file.*    
~/my docs/*.txt
...

Я читаю строки и передаю их команде (например, rsync):

while read ROW
do
   rsync $ROW /my/dest/
done < list.txt

Проблема заключается в обработке пробелов в именах файлов.Если я помещу $ ROW в двойные кавычки, как это

rsync "$ROW" /my/dest/

, то, конечно, bash не экранирует подстановочные знаки и не тильду.Но если я не использую кавычки, пробел разбивает строку.

Одним из возможных решений является изменение IFS (осторожно: сценарий сложнее, чем тот, о котором я сообщал).Другое решение (спасибо за исправление Патрику Эхтербруху) заключается в том, чтобы предварительно покинуть пробелы.Однако следующий код не работает для меня:

while read ROW
do
    export NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') 
    echo "Value: $NEWROW"
    ls -1d $NEWROW
done < list.txt

Обратите внимание, что кавычки не передаются в ls.Файл "~ / abc / test.txt" существует, но я получаю:

Value: ~/saver/a\ b\ c/*.txt
ls: impossibile accedere a ~/saver/a\: Nessun file o directory
ls: impossibile accedere a b\: Nessun file o directory
ls: impossibile accedere a c/*.txt: Nessun file o directory

Похоже, NEWROW передается в ls в виде строки, поэтому подстановочные знаки не раскрываются, но пробел продолжает ломать имя файла, хотясбежал.

Любой совет?Спасибо.

Ответы [ 2 ]

1 голос
/ 18 января 2011

Насколько я вижу, размещенный вами скрипт sed не будет иметь никакого эффекта - части "search" и "replace" одинаковы.

То, что вы можете сделать, может сделать:

ROW=$(echo $ROW | sed -e "s/ /\\\\ /g")

или (лучше):

ROW=$(echo $ROW | sed -e 's/ /\\ /g')

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

Относительно второй проблемычто возникло: специальные символы (например, ~) расширяются с помощью bash, когда они встречаются в параметрах программы.Они не будут расширены при сохранении их в переменной, а также не будут проверять и расширять содержимое переменной при передаче ее в качестве параметра команде.

Именно поэтому rsync $NEWROW ... не работает - rsync получаетнемодифицированное содержимое $ NEWROW в качестве первого параметра и затем должно было бы выполнить расширение, подобное оболочке, самостоятельно.

Единственный известный мне обходной путь - это запустить команду в подоболочке, то есть:

ROW="~/a b c"
$NEWROW=$(echo $ROW | sed -e 's/ /\\ /g')
bash -c "ls -ld $f"

Это заставит текущий запущенный bash извлечь содержимое переменных (т.е. ~/a\ b\ c и передать его второй оболочке, которую он собирается вызвать. Этот "внутренний" bash получит команду вместе с параметрами изатем, конечно, к расширению параметра.

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

Нашел другое возможное решение: после преобразования пути с помощью sed(например, NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') try:

eval ls -ld $NEWROW

или eval rsync $ NEWROW / other / path

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

// РЕДАКТИРОВАТЬ: Добавлено пропущенное g в сценарий sed

// РЕДАКТИРОВАТЬ: Добавлен обходной путь для проблемы расширения пути

// РЕДАКТИРОВАТЬ: Addes eval решение

0 голосов
/ 18 января 2011

Я думаю, что вы можете направить список для rsync в отдельный файл и выполнить его, как

while read ROW
do
  echo "rsync '$ROW' /my/dest/"
done < list.txt > rsync.txt

sh rsync.txt
...