2013-11-09 3 views
1

Часть моего makefile выглядит следующим образом:Прочитайте каждую строку целиком, независимо от пространства

list:   all 
       for f in \ 
       `less fetch/list.txt`; \ 
       do \ 
        echo $$f; \ 
        ... 
       done 

fetch/list.txt содержит список файлов:

path/file1.ml 
path/file2.ml 
path/file 3.ml 
path/file 4.ml 

Проблема заключается в том, даже если пространство допускается в имя файла, make list показывает:

path/file1.ml 
path/file2.ml 
path/file 
3.ml 
path/file 
4.ml 

Кто-нибудь k теперь, чтобы каждая строка была прочитана целиком, независимо от пробелов?

ответ

5

Вот один из способов сделать это:

list: 
     while IFS= read -r n ; do echo "line: $$n" ; done < list.txt 

Здесь в действии:

$ cat list.txt 
abc 
def 
123 456 

$ gmake 
while read n ; do echo "line: $n" ; done < list.txt 
line: abc 
line: def 
line: 123 456 
line: 
+2

Это битва. Вы определенно хотите 'read -r' в случае, если в строках есть обратные косые черты, а также вам нужно знать, что чтение всегда разделяет символы верхнего и нижнего символов IFS. Поэтому вам действительно нужно: 'while IFS = read -r ...'. Или вы можете использовать 'mapfile/readarray -t' – rici

+0

@rici спасибо, обновили ответ с вашими предложениями. –

0

Одним из возможных решений было бы кормить fetch/list.txt к xargs и иметь, которые выполняют ваши команды для каждой строки в файле:

xargs -i sh -c 'echo "${1}" ; ...' _ {} < fetch/list.txt 

В общем for f in [some list of values that might contain spaces] следует подходить с осторожностью в скриптах.

+0

бы уход downvoter объяснить, почему/как этот ответ не был полезен? – pobrelkey

+0

Это решение очень неэффективно, потому что оно запускает новую оболочку для каждой строки ввода, а также труднее читать, чем альтернативу (поскольку 'xargs' использует нестандартные символы, такие как' _' и '{}'). Наконец, это не не-no, чтобы перебирать вещи с пробелами, вы просто должны знать, как это работает, и процитировать его правильно. –

+0

@ AndersJohansson True. Это грязно; Я не думал о том, чтобы решить проблему «в то время как читать». Вам не нужно создавать новую оболочку для каждого файла, но поскольку OP не показывал, что было в цикле, это казалось логичным выбором для шаблона решения. Не уверенность в эффективности - настолько большая проблема, хотя (будет с большим проектом на Cygwin, где нерестится процесс дорого, а не столько в Linux, сколько в нескольких исходных файлах). Сделал небольшое редактирование - спасибо. – pobrelkey

1
for files in 
$(sed -n '/file/p' list.txt) 
do 
echo "${files}" 
... 
done 

Обычно это верно, что связывание команд на выход петли плохая форма, но СЭД обычно может сделать это надежно, потому что он всегда будет процитировать всю линию. Вы должны убедиться, что вы положили в двойные кавычки (") вокруг имени переменной, когда эхо, а также, если вы хотите его вывод в том же виде, что и вход

-1

Это, кажется, работает:.

list:   all 
    for f in "`cat filename`"; \ 
    do \ 
     echo "$$f"; \ 
    done 

all: 
    /bin/true 
1

при работе с вар iables, которые могут содержать пробелы, вы должны процитировать их, чтобы сохранить пробелы. Это будет работать, например:

for f in "$(cat fetch/list.txt)"; do  # preserve spacing from $() 
    echo "$f"       # preserve spacing from $f 
done 

Еще несколько предложений:

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

Но было бы еще эффективнее цикла по содержимому файла непосредственно (как уже было сказано в ответ Эрика), вместо преобразования файла в список элементов перебрать:

while read f; do   # read each line into f 
    echo "$f" 
done < fetch/list.txt  # from fetch/list.txt 

Здесь , read читает целые строки (в то время как замещение процесса и обратные выходы создают списки элементов, а не строки, если вы не цитируете его), поэтому вам нужно только котировки при использовании $f. Обратите внимание на расположение переадресации <: после ключевого слова done, которое может показаться запутанным.

1

Это может работать для вас:

for file in "$(<list.txt)"; do echo "$file"; done 
Смежные вопросы