Вопрос Невозможно использовать файлы со специальными символами в оболочке


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

files=""
for f in $(ls ~/Downloads | grep -v ^d)
        do

                files+="$f"","

        done 
        echo "$files"

здесь папка загрузок имеет файлы, подобные

это: 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Ожидаемый результат: 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Фактический выход: 01, -, Агар, Mujhse, Mohabbat, Хай, -, www.downloadming.com.mp3

Все решения приветствуются.


4
2018-05-08 16:36


происхождения


я думаю это ls ~/Downloads | grep -v ^d будет печатать каждый файл или каталог, который не начинается с d. - Avinash Raj
Используйте zsh - files=(*(.)); echo ${(j:,:)files}, - Kevin


ответы:


Не разбирайте ls

files=()

cd ~/Downloads
for file in *; do
    [[ -d $file ]] && continue
    files+=( "$file" )
done

printf "%s\n" "${files[@]}"

comma_separated=$(IFS=,; echo "${files[*]}")
echo "$comma_separated"

Как вы видели, for f in $(ls) итерации по слова на выходе, а не линии, Сохранить ls когда вы хотите, чтобы человеческие глаза видели файлы и использовали шаблоны файлов оболочки, когда вы хотите, чтобы ваш скрипт что-то делал с именами файлов.


13
2018-05-08 17:00





Более короткое решение: find ~/Downloads -type f -not -name "d*" -printf "%f,"


2
2018-05-08 17:04



ОП использовал grep -v ^d чтобы избежать списков каталогов, ваш подход пропустит все файлы, начиная с d, То, что вы хотели, это find ~/Downloads -type f, - terdon♦
@terdon Как сделать ls dirname | grep -v ^d избегать списков каталогов? - Jos
@terdon dunno you, но в zsh + grep 2.18 + ls 8.21 ls ~/Downloads | grep -v ^d не препятствует спискам каталогов. - Braiam
@Jos Это, конечно, не будет, я полностью неправильно понял. Я думал, что это был ls -l и я прочитал ответ Гленна, который пропускает каталоги, поэтому я и предполагал. Извини, я виноват. - terdon♦
@terdon ОК. Фактически, ОП, возможно, попытался пропустить каталоги, начиная с выполнения ls -l, а затем передумали использовать ls и оставляя ^d но я прочитал его как «пропустить имена файлов, начиная с d». - Jos


использование while read вместо for

Самый безопасный способ, который я нашел для итерации по спискам имен файлов, - это while read Таким образом:

find ~/Downloads -type f | while read filename; do
  echo "found file called $filename";
done

Как вы можете видеть, оно помещает полное имя файла (разделение по строкам, а не пробелу) в имя переменной, которое вы поставляете, здесь $filename,

Однако из исходного вопроса, если действительно требуется, это список файлов, разделенных запятыми, тогда вам даже не нужно выполнять итерацию, просто используйте tr в трразделите разделители строк на запятые:

find ~/Downloads -type f | tr '\n' ','

1
2018-05-09 12:49





Это проблема внутреннего полевого разделителя. Скорее, это не проблема, она была разработана именно так. Он разбивает входные пробелы, как и в позиционных параметрах.

На странице пользователя Bash:

для  имя [[ в [ слово ...]]; ] делать  список ; сделанный

Список следующих слов в расширяется, генерируя список элементов. Переменная имя по очереди устанавливается каждый элемент этого списка и список выполняется каждый раз. Если в  слово  является                   опущено для команда выполняет список один раз для каждого установленного параметра позиционирования. Статус возврата - это статус выхода последней выполняемой команды. Если                   расширение позиций, следующих в приводит к пустому списку, никакие команды не выполняются, а статус возврата равен 0.

Это означает, что он расширяет результаты выражения в список слов. Здесь находится внутренний разделитель полей, $ IFS. По умолчанию для $ IFS задано значение пробела (пробел, табуляция и новая строка). Это означает, что поле состоит из всего, вплоть до (но не включая) этих пробельных символов. Поскольку имена файлов состоят из пробелов, они обрабатывают их как несколько разных полей и запускают цикл один раз для каждого поля. Способ исправить это - настроить значение $ IFS.

Итак, вам нужно настроить $ IFS на что-то другое, кроме пробелов. Используя ваш текущий метод, однако не было бы возможности указать, где заканчивается одно имя файла, а другое начинается, если у всех их нет расширения файлов. Более простой способ сделать это - использовать findполезность:

#Save the original IFS
oldifs=$IFS
#Set the new IFS to the ASCII null character \x00
IFS=$(echo -ne "\x00")

files="" #To prevent scoping issues

#Use the find utility to find and print just regular files (not directories),
#and not going further than ~/Downloads (remove '-maxlength 1' to make the
#search fully recursive), then print the filenames in the format
#file1\x00file2\x00file3\x00file4...

#However, since our new IFS is now \x00, the boundary upon which these filenames are
#separated now works as expected
for f in $(find ~/Downloads -maxdepth 1 -type f -print0)
do
  files="$files,$f"
done
echo "$files"

#Reset IFS
IFS=$oldifs

Я также хотел бы отметить, что ваше заявление ls ~/Downloads | grep -v ^d вероятно, не делает то, что вы ожидаете. Похоже, вы пытались отфильтровать каталоги, но то, что на самом деле происходит здесь, это то, что он отфильтровывает файлы, начинающиеся с 'd'. Это связано с тем, что вы не используете длинную форму ls как ls -l, ls -l печатает вывод как:

drwxrwxrw- user group SomeDirectory size date_modified filename

wers 'ls' печатает только имена файлов:

SomeDirectory
file1
file2
file3

1
2018-05-08 17:12



Я думаю, это не то, чего хочет OP. Если я попробую это, я получу что-то вроде этого ,/home/user/Pictures/file1/home/user/Pictures/file2/home/user/Pictures/file2  цикл кажется ненужным, потому что всегда есть только один вывод для всех файлов вместе. - TuKsn