wc -m, кажется, останавливается, пока цикл в bash - PullRequest
0 голосов
/ 28 февраля 2019

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

У меня есть текстовый файл, который состоит из 1 столбца со случайными именами пользователей.Этот текстовый файл затем используется в качестве параметра для моего bash-скрипта, который в идеале использует имя пользователя для извлечения страницы и подсчета количества символов на этой странице.Если страница получена успешно, то количество символов сохраняется вместе с именем пользователя в другом текстовом файле.

Вот код:

#!/bin/bash
filename=$1

while read username; do
    curl -fs "http://example.website.domain/$username/index.html"
    if [ $? -eq 0 ]
    then
        x=$(wc -m)
        echo "$username $x" > output.txt
    else
        echo "The page doesn't exist"
    fi
done < $filename

Теперь у меня возникла проблема:что после одной успешной выборки он считает символы, выводит их в файл и просто завершает цикл и выходит из программы.Если я удаляю конкретно бит "wc -m", код работает отлично.

Q: Это должно произойти, как мне это сделать, чтобы достичь своей цели?Или я ошибся где-то еще?

Ответы [ 3 ]

0 голосов
/ 28 февраля 2019

Показанный код не выполняет то, что вы думаете (и утверждаете в своем вопросе).

Ваша команда curl выбирает Интернет и выбрасывает его в стандартный вывод: вы не сохраняете эту информацию длябудущее использование .Затем ваш wc не имеет никаких параметров, поэтому начинает читать со стандартного ввода .А в stdin у вас есть список имен пользователей из $filename, поэтому вычисляемое число - это не символы сети, а остальные символы файла.Как только это будет учтено, в stdin ничего не останется для чтения, поэтому цикл заканчивается, потому что он дошел до конца файла.

Вы ищете что-то вроде:

#!/bin/bash
filename="$1"

set -o pipefail
rm -f output.txt
while read username; do
    x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m)
    if [ $? -eq 0 ]
    then
        echo "$username $x" >> output.txt
    else
        echo "The page doesn't exist"
    fi
done < "$filename"

Здесь полученная страница напрямую подается на wc.Если произойдет сбой curl, вы не увидите этого (по умолчанию код выхода серии переданных команд является кодом выхода последней команды), поэтому мы используем set -o pipefail, чтобы получить код выхода самого правого кода выхода с помощьюзначение, отличное от нуля.Теперь вы можете проверить, все ли прошло нормально, и в этом случае вы можете записать результат.

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

Обновление (по популярному запросу):

Шаблон:

<cmd>
if [ $? -eq 0 ]...

обычно плохая идея.Лучше пойти на:

if <cmd>...

Так что было бы лучше, если вы переключитесь на:

if x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m); then
    echo...
0 голосов
/ 28 февраля 2019

Как уже отмечали другие, просто wc будет "зависать", потому что он ожидает, что вы предоставите ввод для stdin.

Вы, похоже, ищете что-то вроде

#!/bin/bash
filename=$1
# Use read -r
while read -r username; do
    if page=$(curl -fs "http://example.website.domain/$username/index.html"); then
        # Feed the results from curl to wc
        x=$(wc -m <<<"$page")
        # Don't overwrite output file on every iteration
        echo "$username $x"
    else
        # Include parameter in error message; print to stderr
        echo "$0: The page for $username doesn't exist" >&2
    fi
# Note proper quoting
# Collect all output redirection here, too
done < "$filename" >output.txt
0 голосов
/ 28 февраля 2019

Программа wc (как и многие другие утилиты, которые вы можете найти в Linux) по умолчанию ожидает, что ее ввод будет предоставлен ей на stdin (стандартный ввод), и выводит ее на stdout (стандартный вывод).).

В вашем случае вы хотите, чтобы wc работал с результатом вашего curl вызова.Этого можно добиться, сохранив результат переменной curl в переменной и передав ее содержимое в wc

data=$(curl -fs "http://example.website.domain/$username/index.html")
...
x=$(echo "$data" | wc -m)

Или вы можете поместить всю команду в один конвейер, что, вероятно,лучше (хотя вы, возможно, захотите set -o pipefail для того, чтобы отлавливать ошибки из curl):

x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m)

В противном случае, как утверждает @Dominique, ваш wc будет ждать, пока не получит какой-либо ввод, бесконечно.

...