Невозможно массировать значения вне функции в сценарии оболочки [duplicate]

Предположим, что вход input.json

Использование Boost Spirit V2.x

Вот упрощенный метод с использованием Boost Spirit Qi:

Live On Coliru

#include 
#include 
#include 
#include 
#include 

namespace {
    using Element = std::map;
    struct Array : std::vector { };

    std::istream& operator>>(std::istream& is, Array& into) {
        using namespace boost::spirit::qi;
        using it = boost::spirit::istream_iterator;

        rule s;
        rule r, e;

        s = '"' >> ~char_('"') >> '"';
        r = (s >> ':' >> int_) % ',';
        e = '{' >> r >> '}';

        return is >> phrase_match('{'
                    >> lit("\"values\"") >> ':' >> '[' >> (e % ',') >> ']'
                >> '}', space, into);
    }
}

int main() {
    std::ifstream ifs("input.json");
    ifs.unsetf(std::ios::skipws);

    Array array;
    if (ifs >> array) {
        std::cout << "Parsed " << array.size() << " elements:\n";

        for (auto& e : array) {
            std::cout << "\n--------------------\n{ ";
            for (auto& kv : e)
                std::cout << "\"" << kv.first << "\": " << kv.second << ", ";
            std::cout << " }\n";
        }
    } else {
        std::cout << "Parsing failed\n";
    }
}

Печатает

std::istream& {anonymous}::operator>>(std::istream&, {anonymous}::Array&)
Parsed 13 elements:
--------------------
{ "A": 1, "B": 10,  }
--------------------
{ "C": 3, "D": 12,  }
--------------------
{ "E": 5, "F": 14,  }
--------------------
{ "G": 7, "H": 16,  }
--------------------
{ "I": 9, "J": 18,  }
--------------------
{ "K": 11, "L": 20,  }
--------------------
{ "M": 13, "N": 22,  }
--------------------
{ "O": 15, "P": 24,  }
--------------------
{ "Q": 17, "R": 26,  }
--------------------
{ "S": 19, "T": 28,  }
--------------------
{ "U": 21, "V": 30,  }
--------------------
{ "W": 23, "X": 32,  }
--------------------
{ "Y": 25, "Z": 34,  }

Использование Spirit X3

То же дело:

Live On Coliru

namespace {
    using Element = std::map;
    struct Array : std::vector { };

    namespace parser {
        using namespace boost::spirit::x3;
        rule s;
        rule r;
        rule  e;

        auto s_def = '"' >> ~char_('"') >> '"';
        auto r_def = (s >> ':' >> int_) % ',';
        auto e_def = '{' >> r >> '}';

        BOOST_SPIRIT_DEFINE(s,r,e)
    }

    std::istream& operator>>(std::istream& is, Array& into) {
        using namespace parser;

        boost::spirit::istream_iterator f(is), l;

        if (!phrase_parse(f, l, '{'
                    >> lit("\"values\"") >> ':' >> '[' >> (e % ',') >> ']'
                >> '}', space, into))
        {
            is.setstate(is.rdstate() | std::ios::failbit);
        }

        return is;
    }
}

Тот же выход с тем же main()

Использование дерева свойств

Это несколько отличается , Я решил не реализовывать operator>>, потому что Boost Property на самом деле этого не дает.

Live On Coliru

#include 
#include 
#include 
#include 

namespace {
    using Element = std::map;
    struct Array : std::vector { };

    Array read(std::string fname) {
        std::ifstream ifs(fname);
        Array into;

        using namespace boost::property_tree;
        ptree pt;
        read_json(ifs, pt);

        for (auto& entry : pt.get_child("values")) {
            Element e;
            for (auto& property : entry.second)
                e[property.first] = property.second.get_value(0);
            into.push_back(std::move(e));
        }

        return into;
    }
}

int main() {
    try {
        auto array = read("input.json");
        std::cout << "Parsed " << array.size() << " elements:\n";

        for (auto& e : array) {
            std::cout << "--------------------\n{ ";
            for (auto& kv : e)
                std::cout << "\"" << kv.first << "\": " << kv.second << ", ";
            std::cout << " }\n";
        }
    } catch (...) {
        std::cout << "Parsing failed\n";
    }
}

Конечно, вывод снова, тот же, что и раньше.

90
задан Matt P 23 September 2008 в 23:16
поделиться

7 ответов

Поскольку вы подключаетесь к циклу while, для запуска цикла while создается вспомогательная оболочка. Теперь этот дочерний процесс имеет свою собственную копию среды и не может передавать какие-либо переменные обратно в родительский (как в любом процессе unix).

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

http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL

93
ответ дан pixelbeat 19 August 2018 в 19:19
поделиться
  • 1
    Имеет смысл. Спасибо. Похоже, что функция - это путь для меня. – Matt P 23 September 2008 в 23:36
  • 2
    это просто ответило на многие из кажущихся случайными проблем, с которыми я столкнулся с сценариями bash. – Daniel Agans 27 January 2015 в 15:29
  • 3
    Этот прекрасный ответ меня так сильно расстраивает и объясняет действительно странное поведение в нашей системе CI. – KayCee 6 April 2017 в 08:29

Я обошел это, когда я делал свой собственный маленький дю:

ls -l | sed '/total/d ; s/  */\t/g' | cut -f 5 | 
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )

. Дело в том, что я создаю подоболочку с (), содержащую мою переменную SUM и while, но я подключаюсь к целое (), а не в то самое время, которое позволяет избежать получения.

0
ответ дан Adrian May 19 August 2018 в 19:19
поделиться

Другой вариант - вывести результаты в файл из подоболочки и затем прочитать его в родительской оболочке. что-то вроде

#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
    LINE="$LINE $line"
    echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)
0
ответ дан David Newcomb 19 August 2018 в 19:19
поделиться
 #!/bin/bash
 OUTPUT="name1 ip ip status"
+export XCODE=0;
 if [ -z "$OUTPUT" ]
----

                     echo "CRIT: $NAME - $STATUS"
-                    echo $((++XCODE))
+                    export XCODE=$(( $XCODE + 1 ))
             else

echo $XCODE

посмотреть, помогают ли эти изменения

1
ответ дан Kent Fredric 19 August 2018 в 19:19
поделиться
  • 1
    При этом я получаю «0», для печати последнего оператора эха. однако я ожидаю, что значение будет 1 не равно нулю. Кроме того, почему использование экспорта? Я предполагаю, что он заставляет его в окружающую среду? – Matt P 23 September 2008 в 23:09

Проблема в том, что процессы, связанные с трубой, выполняются в подоболочках (и, следовательно, имеют собственную среду). Все, что происходит внутри while, не влияет на что-либо вне трубы.

Ваш конкретный пример можно решить, переписав канал на

while ... do ... done <<< "$OUTPUT"

или, возможно,

while ... do ... done < <(echo "$OUTPUT")
97
ответ дан mweerden 19 August 2018 в 19:19
поделиться
  • 1
    Brilliant. Я не видел, & Lt; синтаксис раньше, так что придется читать на нем, но это работает. Кажется странным иметь команду ввода в конце, а не в начале, но она работает. – Jonathan 31 October 2008 в 20:32
  • 2
    Я также не знал синтаксиса <( command ), и это сэкономило мне много времени и головных болей. большой +1 к вам сэр :) – Carlos Campderrós 30 January 2013 в 14:29
  • 3
    Для тех, кто смотрит на это смущение относительно того, что такое синтаксис & lt; () (как и я), он называется «Замена процесса», а конкретное использование, описанное выше, можно увидеть здесь: mywiki. wooledge.org/ProcessSubstitution – Ross Aiken 19 February 2013 в 16:53
  • 4
    спасибо , это именно то, что я искал! – memnoch_proxy 4 April 2014 в 04:37

Еще одна опция:

#!/bin/bash
cat /some/file | while read line
do
  var="abc"
  echo $var | xsel -i -p  # redirect stdin to the X primary selection
done
var=$(xsel -o -p)  # redirect back to stdout
echo $var

EDIT: Здесь требуется xsel (установите его). Кроме того, вы можете использовать xclip: xclip -i -selection clipboard вместо xsel -i -p

3
ответ дан Rammix 19 August 2018 в 19:19
поделиться
  • 1
    Я получаю сообщение об ошибке: ./scraper.sh: строка 111: xsel: команда не найдена ./scraper.sh: строка 114: xsel: команда не найдена – 3kstc 19 April 2016 в 01:57
  • 2
    @ 3kstc, очевидно, установите xsel. Кроме того, вы можете использовать xclip, но его использование немного отличается. Главный пункт здесь: 1-й вы помещаете вывод в буфер обмена (3 из них в Linux), 2-й - вы его отбираете и отправляете на стандартный вывод. – Rammix 9 May 2016 в 20:43

Это должно работать также (потому что эхо и while находятся в одной подоболочке):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )
5
ответ дан sano 19 August 2018 в 19:19
поделиться