У меня есть список файлов, созданных из find. Из-за некоторого плохого планирования со стороны руководства мне теперь нужно проверить эти файлы, но команда поиска заняла несколько часов, так как мы сканируем несколько ТБ данных. Поэтому я планировал просмотреть список файлов со скриптом и запустить статистику на основе файлов, которые мы уже нашли. Проблема в том, что BASH постоянно сообщает, что все файлы не существуют внутри скрипта, но выполнение любой команды с файлами вне скрипта (только на терминале) работает нормально.

EX:

sh ~/temp.sh  | head -1
stat: ./2017\ Digital/redacted\ Projects-redacted-IP.idx: stat: No such file or directory

stat -x ./2017\ Digital/redacted\ Projects-redacted-IP.idx
  File: "./2017 Digital/redacted Projects-redacted-IP.idx"
  Size: 25165824     FileType: Regular File
  Mode: (0500/-r-x------)         Uid: (redacted)  Gid: (redacted)
Device: 47,12   Inode: 440520    Links: 1
Access: Wed Jul  5 17:24:48 2017
Modify: Wed Jul  5 17:24:48 2017
Change: Wed Jul  5 17:24:48 2017

i=0
IFS=$'\n'
for j in $(cat ~/files); do
    stat -x $j
done

Это общие папки Windows, смонтированные в /Volumes / в OSX. Независимо от того, как я, кажется, изменяю цитаты (двойные / одинарные / нет / и т.д.), Скрипт постоянно сообщает, что ВСЕ файлы не существуют. Я также попробовал Python-версию скрипта:

from subprocess import call
f=open( "/Users/me/files", "r" )
for l in f:
    call(["stat","-x","/Volumes/"+l])

С той же ошибкой. Кажется, проблема напрямую связана с пробелами в именах файлов. Поскольку при жестком кодировании другого файла без пробелов он работает просто отлично. Я предполагаю, что мне нужно сделать что-то с цитированием моих переменных по-другому? Но я не могу при всей жизни понять, как правильно процитировать эти переменные, чтобы заставить его работать.

1 ответ1

3

Проблема заключается в том, что пути, перечисленные в ~/files, включают в себя экранирование (обратную косую черту) перед пробелами (и, возможно, другими символами), и они рассматриваются как часть имени файла. Обычно, когда вы вводите такую команду:

stat -x ./2017\ Digital/redacted\ Projects-redacted-IP.idx

процесс синтаксического анализа оболочки интерпретирует обратную косую черту как означающую, что пробелы являются частью аргумента (а не разделителями между аргументами), а затем удаляет их перед передачей аргумента в stat . Таким образом, stat фактически получает два аргумента: -x и ./2017 Digital/redacted Projects-redacted-IP.idx .

С другой стороны, когда вы читаете пути к файлам из файла, он никогда не проходит через этот шаг анализа и удаления, так что escape-значения передаются в stat как часть аргумента, и он ищет файлы, которые на самом деле имеют обратную косую черту в их имена (и не находит их).

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

Оказывается, есть простой способ сделать разбор и удаление escape из содержимого файла. В комментарии я рекомендовал переключиться с цикла for цикл while read while и связал BashFAQ # 1: Как я могу читать файл (поток данных, переменную) построчно (и / или поле за полем)? , Если вы читаете это, он фактически рекомендует использовать while IFS= read -r и говорит:

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

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

#!/bin/bash

i=0
while IFS= read j; do
    stat -x "$j"
done <~/files

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .