В то время как цикл не останавливается и читает последнюю строку (bash) [duplicate]

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

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

В прошлом я был очень удивлен (шокирован), насколько быстро хешсет (или словарь) превосходит итерирование по списку. С строками это смехотворно малое количество (около 5-7 предметов из памяти). С большинством других простых данных (ссылки на объекты, числа и т. Д.) Магический кроссовер, кажется, составляет около 20 элементов.

В списке Path.InvalidFileNameChars имеется 40 недопустимых символов. Был поиск сегодня, и здесь есть неплохой ориентир на StackOverflow, который показывает, что hashset займет чуть более половины времени для массива / списка для 40 элементов: https://stackoverflow.com/a/10762995/949129

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

Дополнительный бонусный метод «IsValidLocalPath» тоже:)

(** те, которые 't использовать регулярные выражения)

  public static class PathExtensions {private static HashSet & lt; char & gt;  _invalidFilenameChars;  private static HashSet & lt; char & gt;  InvalidFilenameChars {get {return _invalidFilenameChars ??  (_invalidFilenameChars = new HashSet & lt; char & gt; (Path.GetInvalidFileNameChars ()));  }} /// & lt; summary & gt; Заменяет символы в & lt; c & gt; text & lt; / c & gt;  которые недопустимы в именах файлов с указанным символом замены ///. & lt; / summary & gt;  /// & lt; param name = "text" & gt; Текст, чтобы ввести действительное имя файла.  Эта же строка возвращается, если /// она уже действительна. & Lt; / param & gt;  /// & lt; param name = "replacement" & gt; Заменяющий символ или NULL для удаления плохих символов. & lt; / param & gt;  /// & lt; param name = "fancyReplacements" & gt; TRUE для замены кавычек и косых черт с символами, отличными от ASCII "и /. & lt; / param & gt;  /// & lt; returns & gt; Строка, которая может использоваться как имя файла.  Если выходная строка в противном случае была бы пустой, возвращается «_». & Lt; / returns & gt;  public static string ToValidFilename (этот текст строки, char? replacement = '_', bool fancyReplacements = false) {StringBuilder sb = new StringBuilder (text.Length);  HashSet & л; символ & GT;  invalids = InvalidFilenameChars;  bool changed = false;  for (int i = 0; i & lt; text.Length; i ++) {char c = text [i];  if (invalids.Contains (c)) {changed = true;  char repl = replacement ??  '\ 0';  if (fancyReplacements) {if (c == '"') repl = '"'; // U + 201D правая двойная кавычка else if (c == '\' ') repl =' ''; // U +  2019 правая одинарная кавычка else if (c == '/') repl = '/'; // U + 2044 fraction slash} if (repl! = '\ 0') sb.Append (repl);} else sb.  Append (c);} if (sb.Length == 0) return "_"; return changed? Sb.ToString (): text;} /// & lt; summary & gt; /// Возвращает TRUE, если указанный путь является  действительный локальный путь файловой системы /// & lt; / summary & gt; /// & lt; param name = "pathString" & gt; & lt; / param & gt; /// & lt; возвращает & gt; & lt; / returns & gt; public static bool IsValidLocalPath (это  string pathString) {// Из решения на https://stackoverflow.com/a/11636052/949129 Uri pathUri; Boolean isValidUri = Uri.TryCreate (pathString, UriKind.Absolute, out pathUri); return isValidUri & amp; & amp; pathUri!  = null & amp; pathUri.IsLoopback;}}  

44
задан RHSeeger 16 October 2012 в 14:58
поделиться

8 ответов

Я подозреваю, что отсутствие новой строки в последней строке вашего файла может вызвать эту проблему. Для тестирования вы можете внести незначительные изменения в свой скрипт и прочитать DATAFILE следующим образом:

while read line
do
    echo $line # do processing here
done < "$DATAFILE"

И посмотрите, не имеет значения.

1
ответ дан anubhava 15 August 2018 в 17:24
поделиться
  • 1
    Хотя я согласен с тем, что никакая окончательная новость не является проблематичной, почему вы думаете, что это будет иначе, чем cat | читать? – Brian 16 October 2012 в 15:18
  • 2
    @Brian: потому что этот сценарий не выполняет какую-либо внешнюю команду, например cat, для ввода данных. – anubhava 16 October 2012 в 15:21

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

echo "\n" >> $file_path

Это обеспечит чтение всех строк, которые были ранее в файле.

0
ответ дан ArunGJ 15 August 2018 в 17:24
поделиться

Используйте sed для соответствия последней строке файла, который затем добавит новую строку, если она не существует, и пусть она выполняет встроенную замену файла:

sed -i '' -e '$a\' file

Код из этой ссылки stackexchange

Примечание: я добавил пустые одинарные кавычки в -i '', потому что, по крайней мере, в OS X, -i использовал -e в качестве расширения файла для файла резервной копии. Я бы с радостью прокомментировал исходный пост, но мне не хватило 50 баллов. Возможно, это принесет мне немного в этой теме, спасибо.

55
ответ дан Community 15 August 2018 в 17:24
поделиться
  • 1
    Спасибо за обширную запись. Я думаю, что разница между поведением двух комманд очень хорошо описана. Я все еще немного запутался в том, почему первая команда терпит неудачу, когда выполняется как часть конвейера, который генерирует файл, но не при запуске независимо. Также стоит отметить, что это поведение, похоже, конфликтует с вашими переживаниями поведения без чтения. Возможно, мне придется вернуться к сценарию и убедиться, что я не неправильно истолковал его результаты. – RHSeeger 16 October 2012 в 17:35
  • 2
    read или read -r? – adrelanos 29 August 2015 в 17:58
  • 3
    @adrelanos: Я использую read, потому что он работал отлично 30 лет назад и до сих пор делает для меня. Современный стиль заключается в использовании read -r, потому что read был запутан процессом POSIX. Ваш звонок. Я не буду обижаться, если вы используете read -r, если вы можете объяснить, что он защищает вас, по сравнению с использованием read, и вы можете объяснить, почему вы заботитесь об этой защите. – Jonathan Leffler 29 August 2015 в 18:10
  • 4
    Благодарю. У меня такая же проблема, и это сработало для меня – Marwen Bkh 17 June 2016 в 14:22

Я проверил это в командной строке

# create dummy file. last line doesn't end with newline
printf "%i\n%i\nNo-newline-here" >testing

Протестируйте вашу первую форму (трубопровод к while-loop)

cat testing | while read line; do echo $line; done

Это пропустит последнюю строку, что имеет смысл поскольку read получает только вход, который заканчивается символом новой строки.


Тест со второй формой (подстановка команды)

for line in `cat testbed1` ; do echo $line; done

Это также возвращает последнюю строку

read получает вход только в том случае, если он завершен символом новой строки, поэтому вы пропустите последнюю строку.

С другой стороны, во второй форме

`cat testing` 

расширяется до формы

line1\nline2\n...lineM 

, которая разделяется оболочкой на несколько полей с использованием IFS, поэтому вы получаете

line1 line2 line3 ... lineM 

Вот почему вы все равно получаете последнюю строку .

p / s: Я не понимаю, как вы получаете первую форму работы ...

0
ответ дан doubleDown 15 August 2018 в 17:24
поделиться
  • 1
    Я вернусь к сценарию и убедитесь, что я не неправильно интерпретирую что-то. Все это было сделано в рамках какой-то работы, с которой я помогаю, и, возможно, мы читаем что-то в нашей спешке, чтобы заставить ее работать. – RHSeeger 16 October 2012 в 17:41

Согласно спецификации POSIX для команды чтения , она должна вернуть ненулевой статус, если «обнаружен конец файла или произошла ошибка». Поскольку EOF обнаружен, когда он читает последнюю «строку», он устанавливает $ line, а затем возвращает статус ошибки, а статус ошибки предотвращает выполнение цикла на этой последней строке. Решение легко: сделайте цикл выполненным, если команда чтения выполнена успешно ИЛИ, если что-то было прочитано в строке $.

while read line || [ -n "$line" ]; do
26
ответ дан Gordon Davisson 15 August 2018 в 17:24
поделиться
  • 1
    +1: Интересное наблюдение, Гордон. Используя мой файл примера y, я побежал: while read line; do echo $line; done < y; echo $line и получил четыре разных значения. Я не уверен, что это особенно полезное или интуитивное поведение, но ... – Jonathan Leffler 16 October 2012 в 18:04
  • 2
    это решило мою проблему чтения слов из текстового файла без наличия новой строки в конце текстового файла. – tauseef_CuriousGuy 3 May 2018 в 15:55

У меня была аналогичная проблема. Я делал кошку файла, соединяя его в сортировку и затем передавая результат в «while read var1 var2 var3». т.е.: cat $ FILE | sort -k3 | при чтении Count IP Name do Работа под «do» была оператором if, который идентифицировал изменение данных в поле $ Name и основан на изменении или без изменений составили суммы в $ Count или распечатали суммированную строку отчета. Я также столкнулся с проблемой, когда я не смог получить последнюю строку для печати в отчете. Я пошел с простой возможностью перенаправить cat / sort в новый файл, повторив новую строку для этого нового файла, и THEN запустил мое «пока прочитанное количество IP-адресов» в новом файле с успешными результатами. ie: cat $ FILE | sort -k3> NEWFILE echo "\n" >> NEWFILE cat NEWFILE | при чтении Count IP Name do Иногда простой, неэлегантный - лучший способ пойти.

0
ответ дан Gulesbaron 15 August 2018 в 17:24
поделиться
55
ответ дан Community 5 September 2018 в 16:29
поделиться
57
ответ дан Community 29 October 2018 в 00:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: