Как я могу избежать пробелов в списке петли bash? - PullRequest
117 голосов
/ 19 ноября 2008

У меня есть скрипт оболочки bash, который перебирает все дочерние каталоги (но не файлы) определенного каталога. Проблема в том, что некоторые имена каталогов содержат пробелы.

Вот содержимое моего тестового каталога:

$ls -F test
Baltimore/  Cherry Hill/  Edison/  New York City/  Philadelphia/  cities.txt

И код, который проходит по каталогам:

for f in `find test/* -type d`; do
  echo $f
done

Вот вывод:

test/Baltimore
test/Cherry
Hill
test/Edison 
test/New
York
City
test/Philadelphia

Черри-Хилл и Нью-Йорк считаются 2 или 3 отдельными записями.

Я попытался процитировать имена файлов, например:

for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
  echo $f
done

но безрезультатно.

Должен быть простой способ сделать это.


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

Я принял предложение Чарльза о настройке IFS и придумал следующее:

dirlist="${@}"
(
  [[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
  for d in $dirlist; do
    echo $d
  done
)

и это прекрасно работает, если в аргументах командной строки нет пробелов (даже если эти аргументы указаны в кавычках). Например, вызов сценария следующим образом: test.sh "Cherry Hill" "New York City" дает следующий вывод:

Cherry
Hill
New
York
City

Ответы [ 20 ]

2 голосов
/ 19 ноября 2008

Чтобы добавить к тому, что сказал Джонатан : используйте параметр -print0 для find в сочетании с xargs следующим образом:

find test/* -type d -print0 | xargs -0 command

Это выполнит команду command с правильными аргументами; каталоги с пробелами в них будут должным образом заключены в кавычки (то есть они будут переданы как один аргумент).

1 голос
/ 10 июля 2009

Приходилось также иметь дело с пробелами в путевых именах. В конце концов я использовал рекурсию и for item in /path/*:

function recursedir {
    local item
    for item in "${1%/}"/*
    do
        if [ -d "$item" ]
        then
            recursedir "$item"
        else
            command
        fi
    done
}
1 голос
/ 18 ноября 2010
#!/bin/bash

dirtys=()

for folder in *
do    
 if [ -d "$folder" ]; then    
    dirtys=("${dirtys[@]}" "$folder")    
 fi    
done    

for dir in "${dirtys[@]}"    
do    
   for file in "$dir"/\*.mov   # <== *.mov
   do    
       #dir_e=`echo "$dir" | sed 's/[[:space:]]/\\\ /g'`   -- This line will replace each space into '\ '   
       out=`echo "$file" | sed 's/\(.*\)\/\(.*\)/\2/'`     # These two line code can be written in one line using multiple sed commands.    
       out=`echo "$out" | sed 's/[[:space:]]/_/g'`    
       #echo "ffmpeg -i $out_e -sameq -vcodec msmpeg4v2 -acodec pcm_u8 $dir_e/${out/%mov/avi}"    
       `ffmpeg -i "$file" -sameq -vcodec msmpeg4v2 -acodec pcm_u8 "$dir"/${out/%mov/avi}`    
   done    
done

Приведенный выше код преобразует файлы .mov в .avi. Файлы .mov находятся в разных папках и имена папок также имеют пробелы . Мой скрипт выше преобразует файлы .mov в файл .avi в той же самой папке. Я не знаю, поможет ли это вам, народы.

Корпус:

[sony@localhost shell_tutorial]$ ls
Chapter 01 - Introduction  Chapter 02 - Your First Shell Script
[sony@localhost shell_tutorial]$ cd Chapter\ 01\ -\ Introduction/
[sony@localhost Chapter 01 - Introduction]$ ls
0101 - About this Course.mov   0102 - Course Structure.mov
[sony@localhost Chapter 01 - Introduction]$ ./above_script
 ... successfully executed.
[sony@localhost Chapter 01 - Introduction]$ ls
0101_-_About_this_Course.avi  0102_-_Course_Structure.avi
0101 - About this Course.mov  0102 - Course Structure.mov
[sony@localhost Chapter 01 - Introduction]$ CHEERS!

ура!

1 голос
/ 18 сентября 2012

Преобразование списка файлов в массив Bash. Здесь используется подход Мэтта МакКлюра для возврата массива из функции Bash: http://notes -matthewlmcclure.blogspot.com / 2009/12 / вытяжным массив из-Баш-функции V-2.html Результатом является способ преобразования любого многострочного ввода в массив Bash.

#!/bin/bash

# This is the command where we want to convert the output to an array.
# Output is: fileSize fileNameIncludingPath
multiLineCommand="find . -mindepth 1 -printf '%s %p\\n'"

# This eval converts the multi-line output of multiLineCommand to a
# Bash array. To convert stdin, remove: < <(eval "$multiLineCommand" )
eval "declare -a myArray=`( arr=(); while read -r line; do arr[${#arr[@]}]="$line"; done; declare -p arr | sed -e 's/^declare -a arr=//' ) < <(eval "$multiLineCommand" )`"

for f in "${myArray[@]}"
do
   echo "Element: $f"
done

Этот подход работает, даже когда присутствуют плохие символы, и является общим способом преобразования любого ввода в массив Bash. Недостатком является то, что если ввод длинный, вы можете превысить пределы размера командной строки Bash или использовать большие объемы памяти.

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

Мне также не нравится установка IFS, он может испортить другой код.

0 голосов
/ 15 января 2016
find Downloads -type f | while read file; do printf "%q\n" "$file"; done
0 голосов
/ 24 августа 2013

Мне нужна была та же концепция для последовательного сжатия нескольких каталогов или файлов из определенной папки. Я решил использовать awk для разбора списка из ls и чтобы избежать проблемы пробела в имени.

source="/xxx/xxx"
dest="/yyy/yyy"

n_max=`ls . | wc -l`

echo "Loop over items..."
i=1
while [ $i -le $n_max ];do
item=`ls . | awk 'NR=='$i'' `
echo "File selected for compression: $item"
tar -cvzf $dest/"$item".tar.gz "$item"
i=$(( i + 1 ))
done
echo "Done!!!"

что ты думаешь?

0 голосов
/ 25 мая 2009

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

test.sh "Cherry Hill" "New York City"

чтобы распечатать их по порядку

for SOME_ARG in "$@"
do
    echo "$SOME_ARG";
done;

обратите внимание, что $ @ окружен двойными кавычками, некоторые примечания здесь

0 голосов
/ 21 апреля 2010

Только что возникла проблема с простым вариантом ... Конвертировать файлы с типом .flv в .mp3 (зевать).

for file in read `find . *.flv`; do ffmpeg -i ${file} -acodec copy ${file}.mp3;done

рекурсивно находит все пользовательские флэш-файлы Macintosh и превращает их в аудио (копирование, без перекодирования) ... это похоже на приведенное выше, отмечая, что чтение вместо "только для файла в " завершится. *

0 голосов
/ 11 апреля 2016

Ну, я вижу слишком много сложных ответов. Я не хочу передавать вывод утилиты find или писать цикл, потому что в find есть опция "exec" для этого.

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

Я взялся за это так:

 find . -name \*.dbf -print0 -exec mv '{}'  . ';'

выглядит очень просто для меня

0 голосов
/ 13 июля 2011

Для меня это работает, и это в значительной степени "чисто":

for f in "$(find ./test -type d)" ; do
  echo "$f"
done
...