не работает скрипт (не пойму почему обнуляются переменные)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Аватара пользователя
Mull
Сообщения: 35
ОС: Ubuntu

не работает скрипт

Сообщение Mull »

в цикле while с 41 строки по 187 переменные
LASTLINE
LASTLINE_V2
HEADERLINENUM
HEADERLINENUM_V2
имеют значение, но на 189-й сразу равны нулю. почему? ведь верхний цикл for должен выполнятся 1 раз на 1 файл, т.е. строчки которые были до while в цикле for должны выполнится 1 раз. разве не так?
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Спасибо сказали:
Аватара пользователя
yars
Сообщения: 1147
Статус: Slacker!
ОС: Slackware64-current

Re: не работает скрипт

Сообщение yars »

Выложите пожалуйста, tar, сжатый одним из компрессоров: gz, bzip2, xz. RAR в линуксе - не "родной" архив, поэтому у некоторых могут быть проблемы с распаковкой вашего файла.
Slackware64-current/Xfce/Xiaomi Mi Notebook Pro 15.6 | Arch Linux/Xfce/Lenovo G580
-------------
Registered Linux User #557010
Спасибо сказали:
Аватара пользователя
Mull
Сообщения: 35
ОС: Ubuntu

Re: не работает скрипт

Сообщение Mull »

yars писал(а):
22.11.2012 18:14
Выложите пожалуйста, tar, сжатый одним из компрессоров: gz, bzip2, xz. RAR в линуксе - не "родной" архив, поэтому у некоторых могут быть проблемы с распаковкой вашего файла.

сделал =)
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5405
ОС: Gentoo

Re: не работает скрипт

Сообщение /dev/random »

Mull писал(а):
22.11.2012 18:36
сделал =)

Вы выложили несжатый. Для 20-килобайтного файла это особого значения не имеет, но на будущее, не забывайте про сжатие.

Ошибка у вас в следующем. В большинстве оболочек, включая bash, команды, входящие в пайплайн, выполняются в подоболочках. Цикл у вас - последняя команда пайплайна. Переменные, само собой, за пределы подоболочки выйти не могут.

Если вы используете bash, то можете указать "shopt -s lastpipe", что заставит последнюю команду каждого пайплайна выполняться без подоболочки, но в этом случае в других оболочках ваш скрипт выполняться не сможет. Единственный способ решить проблему универсальным, не зависящим от оболочки образом - сохранять вывод левой команды во временный файл или переменную и затем обрабатывать этот файл правой командой, без пайпов.

Upd: исправил опечатку.
Спасибо сказали:
Аватара пользователя
Mull
Сообщения: 35
ОС: Ubuntu

Re: не работает скрипт

Сообщение Mull »

/dev/random писал(а):
22.11.2012 19:09
Mull писал(а):
22.11.2012 18:36
сделал =)

Вы выложили несжатый. Для 20-килобайтного файла это особого значения не имеет, но на будущее, не забывайте про сжатие.

Ошибка у вас в следующем. В большинстве оболочек, включая bash, команды, входящие в пайплайн, выполняются в подоболочках. Цикл у вас - последняя команда пайплайна. Переменные, само собой, за пределы подоболочки выйти не могут.

Если вы используете bash, то можете указать "shopt -s subpipe", что заставит последнюю команду каждого пайплайна выполняться без подоболочки, но в этом случае в других оболочках ваш скрипт выполняться не сможет. Единственный способ решить проблему универсальным, не зависящим от оболочки образом - сохранять вывод левой команды во временный файл или переменную и затем обрабатывать этот файл правой командой, без пайпов.

не могли бы вы подробнее рассказать про shopt -s subpipe? в интернете ничего не нарыл =( а консоль на эту команду выдает

Код: Выделить всё

napalm@home:~/work$ shopt -s subpipe
-bash: shopt: subpipe: недопустимое имя опции оболочки
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: не работает скрипт

Сообщение sgfault »

Как вам уже ответил /dev/random проблема в том, что ваш цикл выполняется в subshell-е. Можете попробовать использовать Process substitution для баша

Код: Выделить всё

$ ls -lh test.txt
-rw-r----- 1 sgf sgf 50M 11\346\234\210 22 22:33 test.txt
$ cat ./t.sh
#!/bin/bash

i=1
while read line; do
    i=$((i + 1))
done < <(cat 'test.txt')
echo "$i"
$ bash ./t.sh
187758


или fifo для sh

Код: Выделить всё

$ cat t2.sh
#!/bin/sh

i=1
rm -f 1.tmp
mkfifo 1.tmp
cat 'test.txt' > 1.tmp &
while read line; do
    i=$((i + 1))
done < ./1.tmp
rm 1.tmp
echo "$i"
$ sh ./t2.sh
187752


В обоих случаях subshell для while создаваться не должен.

Upd1.
Оба предыдущих примера - вариант решения с файлами, предложенный /dev/random-ом. Еще один способ - выводить необходимые переменные окружения на stdout перед завершением subshell-а, и потом устанавливать их с помощью eval-а в основном окружении:

Код: Выделить всё

$ cat ./t3.sh
#!/bin/sh

cycle()
{
    while [ 0 ]; do
        if ! read line; then
           echo "i=\"$i\""
           break
        fi
        i=$((i + 1))
    done
}

i=1
eval "$(cat 'test.txt' | cycle)"
echo "$i"
$ sh ./t3.sh
187752

Если в значении переменной i будут кавычки, то этот способ приведет к непредсказуемым последствиям. Вот пример, который выполняет команду ls и устанавливает _две_ переменных вместо предполагаемой одной с помощью специально подобранного значения переменной i:

Код: Выделить всё

$ ( eval "$( i='1"; ls; v="2'; echo i="\"$i\""; )"; echo "i=$i, v=$v" )
fsdf fsdf.csv  report_v.2.0.sh  t2.sh  t3.sh  t4.sh  test.txt  t.sh  work.rar  work.tar
i=1, v=2


И, наконец, еще один способ: просто не использовать конструкцию ' | while read'. Если файл небольшой, можно его содержимое подставить через command substitution "напрямую":

Код: Выделить всё

$ cat ./t4.sh
#!/bin/sh

nl='
'

set -f
IFS="$nl"
for line in $(cat 'test.txt'); do
    i=$((i + 1))
done
echo "$i"


Для файла в 50мб самым быстрым оказался последний способ, если выполнять в dash:

Код: Выделить всё

$ ll -h test.txt
-rw-r----- 1 sgf sgf 50M 11\346\234\210 22 22:33 test.txt
$ time bash ./t.sh
187758

real    0m32.312s
user    0m11.493s
sys     0m20.877s
$ time sh ./t2.sh
rm: cannot remove `1.tmp': No such file or directory
187752

real    0m24.954s
user    0m4.680s
sys     0m20.329s
$ time sh ./t3.sh
187752

real    0m25.377s
user    0m4.688s
sys     0m20.645s
$ time sh ./t4.sh
187879

real    0m1.273s
user    0m1.040s
sys     0m0.304s
$ time bash ./t4.sh
187879

real    0m18.947s
user    0m18.501s
sys     0m0.460s

(обратите внимание, что разница между временем для ./t4.sh в dash и bash отличается больше, чем в 9 раз)

И еще интересный вопрос - почему у этих скриптов отличаются ответы :happy: (если что, я понятия не имею, файл был бинарным).
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5405
ОС: Gentoo

Re: не работает скрипт

Сообщение /dev/random »

Mull писал(а):
22.11.2012 21:21
не могли бы вы подробнее рассказать про shopt -s subpipe? в интернете ничего не нарыл =( а консоль на эту команду выдает

Прошу прощения, я опечатался. lastpipe, конечно.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: не работает скрипт

Сообщение drBatty »

Mull писал(а):
22.11.2012 17:53
в цикле while с 41 строки по 187 переменные

вот-вот... А мне тут ещё рассказывают, что конструкция cat file| while read; do; done имеет право на существование...

в данном случае я-бы разорился на временный файл, который-бы и отправил в цикл.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Mull
Сообщения: 35
ОС: Ubuntu

Re: не работает скрипт

Сообщение Mull »

drBatty писал(а):
23.11.2012 20:52
Mull писал(а):
22.11.2012 17:53
в цикле while с 41 строки по 187 переменные

вот-вот... А мне тут ещё рассказывают, что конструкция cat file| while read; do; done имеет право на существование...

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

Спасибо, так и сделал =)
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: не работает скрипт

Сообщение ZyX »

/dev/random писал(а):
22.11.2012 19:09
Единственный способ решить проблему универсальным, не зависящим от оболочки образом - сохранять вывод левой команды во временный файл или переменную и затем обрабатывать этот файл правой командой, без пайпов.
Не‐а. Ещё можно загнать весь (то есть, оба конца) pipe в VAR="$(…)" и обрабатывать $VAR. Для того, чтобы команды могли использовать stdout есть

(Created by format.vim)

exec 3>&1 VAR="$(echo Message >&3)"

. Работает в dash и bb, значит, скорее всего, является стандартным.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5405
ОС: Gentoo

Re: не работает скрипт

Сообщение /dev/random »

ZyX писал(а):
31.12.2012 21:51
Не‐а. Ещё можно загнать весь (то есть, оба конца) pipe в VAR="$(…)" и обрабатывать $VAR. Для того, чтобы команды могли использовать stdout есть

(Created by format.vim)

exec 3>&1 VAR="$(echo Message >&3)"

. Работает в dash и bb, значит, скорее всего, является стандартным.

И как вы предлагаете это применить к проблеме топикстартера? Ему нужно [было месяц назад], чтобы куча переменных, получающих значения в цикле, который является последней частью пайпа, сохраняла эти значения после цикла. Вы предлагаете обе части, т.е. и цикл тоже, вставить в subshell substitution. И чем это поможет переменным, получившим значения внутри цикла?
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: не работает скрипт

Сообщение ZyX »

/dev/random писал(а):
31.12.2012 22:10
ZyX писал(а):
31.12.2012 21:51
Не‐а. Ещё можно загнать весь (то есть, оба конца) pipe в VAR="$(…)" и обрабатывать $VAR. Для того, чтобы команды могли использовать stdout есть

(Created by format.vim)

exec 3>&1 VAR="$(echo Message >&3)"

. Работает в dash и bb, значит, скорее всего, является стандартным.

И как вы предлагаете это применить к проблеме топикстартера? Ему нужно [было месяц назад], чтобы куча переменных, получающих значения в цикле, который является последней частью пайпа, сохраняла эти значения после цикла. Вы предлагаете обе части, т.е. и цикл тоже, вставить в subshell substitution. И чем это поможет переменным, получившим значения внутри цикла?
Выдаёте результат, для которого можно делать eval (например, просто set, если не смущает количество лишних переменных), делаете eval результата, в чём проблемы? Это просто общий метод получения значений из pipe. Нет никакой разницы, получать ли оттуда значение одной переменной или всех сразу.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: не работает скрипт

Сообщение drBatty »

ZyX писал(а):
01.01.2013 03:33
Выдаёте результат, для которого можно делать eval (например, просто set, если не смущает количество лишних переменных), делаете eval результата, в чём проблемы?

eval штука опасная, и обычно можно и без неё обойтись. Например не нужно пайп пихать в цикл.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали: