Есть ли способ удалить не все, но только вложенные брекеты?

Открытый )ssh-клиент (попытается прочитать определенные имена файлов , предполагая, что они содержат ваш закрытый ключ:

~/.ssh/identity
~/.ssh/id_dsa
~/.ssh/id_ecdsa
~/.ssh/id_ed25519
~/.ssh/id_rsa
    Contains the private key for authentication. 

Поскольку у вас есть каталогid_rsa, вам нужно будет использовать опцию -i, чтобы указать ssh, где находится ваш закрытый ключ:

-i identity_file

   Selects a file from which the identity (private key) for public key authentication is read. 

Либо переместите свои закрытые ключи напрямую в каталог ~/.ssh/, стараясь временно переименовать любой файл ключа id_rsa, чтобы можно было удалить пустой каталог id_rsa. Если вы затем назовете свой ключ (s )с именем файла, которое ожидает ssh, вы можете избежать необходимости указывать -iвсе время.

9
19.06.2020, 21:47
10 ответов

bracket.awk:

BEGIN{quote=1}
{
    for(i=1;i<=length;i++){
        ch=substr($0,i,1)
        pr=1
        if(ch=="\""){quote=!quote}
        else if(ch=="[" && quote){brk++;pr=brk<2}
        else if(ch=="]" && quote){brk--;pr=brk<1}
        if(pr){printf "%s",ch}
    }
    print ""
}
$ awk -f bracket.awk file
["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]

Идея:

Инициализировать quote=1. Прочтите файл char -. Всякий раз, когда кавычка найдена, инвертируйте quoteпеременную (, если 1, она становится 0, и наоборот -наоборот ).

Затем скобки подсчитываются, только если quoteустановлено на 1 и лишние скобки не печатаются в соответствии со счетчиком brk.

Оператор print ""просто добавляет новую строку, а оператор printfвыше этого не делает.

12
18.03.2021, 23:26

(Этот ответ является скорее экспериментом, чтобы увидеть, как будет выглядеть «применение правильного инструмента для работы» — в данном случае clojure. Действительно, я пришел написать этот ответ именно потому, что решение в clojure пришло мне в голову примерно через 10 секунд после прочтения вопроса, в сочетании с фактом о (read), о котором я упомяну ниже. Остальное - настоящая «проблема» в этом ответе - было 90-минутной попыткой бороться с интерактивными корнями шепелявости. Эта борьба мне знакома; SML, особенно его реализация в Нью-Джерси, страдает тем же недостатком.)

Лисп — очевидный выбор для обработки структур данных, -таких как списки. На самом деле в clojure эта проблема решается с помощью (flatten my-list)или (map flatten list-of-lists)!

Но мы еще не закончили, если хотим, чтобы ввод и вывод точно соответствовали тому, что определено в вопросе. Здесь clojure борется под собственным весом :он был создан для интерактивного использования (как многие lisps)или для использования в качестве java-программы (запускает основной метод ). Ни один из них на самом деле не облегчает традицию фильтрации unix по чтению со стандартного ввода / записи на стандартный вывод. Итак, мы решим эту задачу несколькими разными способами, каждый из которых будет более или менее полезным/возмутительным.

Мы будем опираться на следующие интересные факты:

  1. Запятые — это пробелы в clojure, поэтому входные данные на самом деле являются допустимым набором векторов clojure.
  2. (read)читает один объект из стандартного in; не строка, как во многих других языках, а отдельная форма clojure (, такая как выражение s -или вектор ).

Метапрограммирование сcat

Уже заметив, что исходный ввод является допустимым clojure, мы обходим его чтение как ввод из clojure, вводя его непосредственно в программу clojure (и оставляем sedдля довольно скучного форматирования):

#! /usr/bin/env bash

clojure -e '(->> ['"$(cat)"'] (map flatten) (map vec) (apply prn))' \
  | sed -e 's/ /, /g'

Выполнение этого приводит к

$./nested-clj-cat <unix.in
["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]

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

Использование clojureв качестве интерпретатора

Разве этот внутренний скрипт clojure не был бы намного удобнее в сопровождении, если бы мы могли переместить его в файл, содержащий код, а не строку в кавычках? (Я говорю это совершенно серьезно, несмотря на традицию в большинстве сценариев оболочки вызывать awk/ sed/ python/ perlвстроенные строки!)

Но теперь мы должны иметь дело со стандартом чтения в; к сожалению, (read)считывает только один объект за раз, в то время как входные данные представляют собой серию объектов.Мы могли бы массировать данные, добавляя [в начало и ]в конец:

sed -e 's/^/[/' -e 's/$/]/'

Но тогда вызывающая сторона должна помнить об этом, иначе исходная программа должна быть скорректирована.

Итак, мы создадим функцию read-all, которая считывает все объекты в потоке и возвращает эту последовательность. Затем мы применим нашу технику из предыдущего:

#! /usr/bin/env clojure

(require '[clojure.java.shell :as shell])

(defn read-all
  [stream]
  (loop [acc []]
    (let [red (binding [*read-eval* false]
                (read {:eof :eof} stream))]
      (if (= red :eof)
        acc
        (recur (conj acc red))))))

(->> (read-all *in*)
     (map flatten)
     (map vec)
     (apply prn))

У этого есть еще один недостаток, :нам по-прежнему нужно sedв конце, чтобы получить точные данные! В противном случае:

$./nested-clj-read <unix.in
["q" "0" "R" "L"] ["q" "1" "[" "]"] ["q" "2" "L" "R"] ["q" "3" "R" "L"]

что просто не совсем правильно. Возможно, мы можем исправить это в clojure?

Топси -вывернутая наизнанку :оболочка в кложуре

Оказывается, верно одно из следующего:

  • Я очень плохо форматирую строки в clojure с помощью простых приемов (str,format)или
  • Clojure ужасно плохо форматирует сложные структуры данных -в виде строк

Я подозреваю последнее только потому, что clojure позволяет очень легко передавать структуры данных -между программами в качестве доказательства структуры данных -(prn/readи формата EDN ). Я не возился с обычным -средством форматирования лиспов cl-format, которое, как я знаю, способно на это, потому что я полагал, что это может быть слишком много шепеляваний в одной и той же мешанине языков :)

. ]

Если кто-то может решить это более элегантно, я был бы рад обсудить это.

В конце концов, я прибег к внедрению вызовов sedвнутрь clojure — это избавляет вызывающую сторону от необходимости помнить о ее вызове за счет еще большего усложнения кода. Чтобы все было красиво и читабельно, я ввожу макрос канала:

(defmacro |
  [cmd in]
  `(:out (shell/sh ~@cmd :in ~in)))

Это должен быть макрос, потому что applyне будет работать со списками, отличными от -, после списков, и я действительно хочу, чтобы inбыл последним параметром (, чтобы он подходит к->>). Увы,из-за реализации shс использованием фьючерсов нам нужен вызов (shutdown-agents), чтобы исключить ожидание в течение нескольких минут после завершения сценария, чтобы он завершился.

Таким образом, финальный сценарий

#! /usr/bin/env clojure

(require '[clojure.java.shell :as shell])

(defn read-all
  [stream]
  (loop [acc []]
    (let [red (binding [*read-eval* false]
                (read {:eof :eof} stream))]
      (if (= red :eof)
        acc
        (recur (conj acc red))))))

(defmacro |
  [cmd in]
  `(:out (shell/sh ~@cmd :in ~in)))

(->> (read-all *in*)
     (map flatten)
     (map vec)
     (apply prn-str)
     (| ["sed" "-e" "s/ /, /g"])
     print)

; needed because of shell/sh's use of futures
(shutdown-agents)

И результаты:

$./nested-clj-read-with-sed <unix.in
["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]

Отлично.

Извлеченные уроки

Другие языки обладают огромными преимуществами, когда речь идет о правильном рекурсивном манипулировании данными. Однако они не всегда упрощают работу в качестве unix-фильтра -, и попытка втиснуть их в этот мир часто приводит к увеличению сложности. Даже короткое catрешение должно заставить рецензента задуматься — не потому, что его трудно понять, а потому, что оно достаточно неясно и требует некоторого размышления.

Тем не менее, возможно, стоит рассмотреть другие языки при манипулировании определенными формами данных :, в то время как все другие решения sed/perl/awk, которые я просматривал здесь, не имели проблем со чтением и записью данных, им приходилось делать совсем немного работы, чтобы манипулировать ею. В некоторых случаях я бы назвал этот уровень работы непосильным из-за сложности, необходимой для понимания команд (, и я сам ежедневно использую эти инструменты )! Это не означает, что мои решения clojure менее непроницаемы, а скорее то, что у нас есть две стороны медали, которая действительно хочет быть тором :дайте мне и то, и другое, дайте мне простоту знания фильтров ввода-вывода и простота обработки данных, присущая lisps/ML.

В сторону :Интересно, сможет ли jqрешить эту проблему после того, как мы обернем ввод в {}?

2
18.03.2021, 23:26

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

Таким образом, мы сохраняем строку в текстовом файле и вызываем ее внутри perl-кода. Что он делает, так это бесплатно создает для нас действующую структуру данных Perl.

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

#! /bin/env bash 
echo '@::LoLs = ([[["q", "0"], "R"], "L"], ["q", [["1", "["], "]"]], [["q", ["2", "L"]], "R"], ["q", ["3", ["R", "L"]]])' > code.pl

perl -wMstrict   -le '
  local $" = ", "; #list separator 
  use constant A => q[ARRAY];
  do "./code.pl";
  print join q[, ], map { qq([$_]) } 
    map {
      $_[0] ||= sub {
        "@{[map {
            +ref eq A ? $_[0]->($_[0],@$_) : qq(\"$_\");
          } splice @_,1]}"; #end inner map
    };  #end sub
    $_[0]->($_[0],$_);
  } @::LoLs; # end outer map 
'

Результат:

["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]
0
18.03.2021, 23:26

Я опубликовал этот вариант только потому, что вы сказали:

I understand how an algorithm could be written that does this by pushing and popping a stack or just incrementing and decrementing a counter

На самом деле я бы просто использовал счётчик.

$ cat tst.awk
{
    $0 = encode($0)
    sep = ""
    while ( match($0,/\[[^][]+]/) ) {
        if ( prevRstart && (RSTART > prevRstart) ) {
            printf "%s%s", sep, decode(prevStr)
            sep = ", "
        }
        prevStr = substr($0,RSTART,RLENGTH)
        prevRstart = RSTART
        $0 = substr($0,1,RSTART-1) "<" substr($0,RSTART+1,RLENGTH-2) ">" substr($0,RSTART+RLENGTH)
    }
    printf "%s%s\n", sep, decode(prevStr)
}

function encode(str) {
    gsub(/@/,"@A",str)
    gsub(/[{]/,"@B",str)
    gsub(/}/,"@C",str)
    gsub(/</,"@D",str)
    gsub(/>/,"@E",str)
    gsub(/"\["/,"{",str)
    gsub(/"]"/,"}",str)
    return str
}

function decode(str) {
    gsub(/[<>]/,"",str)
    gsub(/}/,"\"]\"",str)
    gsub(/[{]/,"\"[\"",str)
    gsub(/@E/,">",str)
    gsub(/@D/,"<",str)
    gsub(/@C/,"}",str)
    gsub(/@B/,"{",str)
    gsub(/@A/,"@",str)
    return str
}

.

$ awk -f tst.awk file
["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]

См.https://stackoverflow.com/a/35708616/1745001для получения дополнительной информации о том, что эти подпрограммы ()s (в этом вопросе )делают для кодирования/декодирования этих значимых символов и строк, необходимых для того, чтобы иметь возможность изолировать [...]струны.

Таким образом, -он находит [...]строки изнутри наружу, другими словами, учитывая [ [ foo ] ]match("[ [ foo ] ]",/[[^][]/)соответствует [ foo ], затем мы меняем [на <и ]до >, так что в следующий раз в цикле match("[ < foo > ]",/[[^][]/)соответствует всей строке. Затем нам просто нужно удалить <и >перед печатью [ foo ]. Он узнает, что найден самый внешний [...], когда в следующий раз в цикле совпадающая строка начинается в позиции за пределами предыдущей начальной позиции (, т.е. не находится внутри предыдущей совпадающей строки ), и в это время она печатает все, что предыдущая совпадающая строка была.

3
18.03.2021, 23:26

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

Но так как это более или менее работает (по средам и пятницам в месяцы с Jв них )и занимает 20 минут моей жизни, я все равно публикую это

Шредингераawk(Спасибо @edmorton)

awk -F"\\\], \\\[" '
    {printf "["; 
       for (i=1; i<=NF; i++) {
         cs=split($i,c,","); 
           for (j=1; j<=cs; j++){
             sub("^ *\\[+","",c[j]); sub("\\]+$","",c[j]);
             t=(j==cs)?"]"((i<(NF-1))?", [":""):",";
             printf c[j] t
       }}print ""}' file

["q", "0", "R", "L"], ["q","1", "[", "]"], ["q","2", "L", "R"], ["q","3","R", "L"]

Прохождение

Разделите поля -Fна ], [, которые нужно сбежать в ад и обратно, чтобы получить ваши окончательные группы элементов в полях.

Затем splitна ,, чтобы получить элементы и использовать все начальные ^[или конечные ]$из каждого элемента,затем повторно -агрегируйте splitс ,в качестве разделителя и, наконец, повторно -агрегируйте поля, используя условную комбинацию ]и , [.

Гейзенбергаsed

Если вы перейдете к sed, это будет немного опрятнее

awk 'BEGIN{FS="\\], \\["}{for (i=1; i<=NF; i++) print $i}' file | 
   sed -E "s/(^| |,)\[+(\")/\1\2/g ;s/\]+(,|$)/\1/g" | 
   awk 'BEGIN{RS=""; FS="\n";OFS="], ["}{$1=$1; print "["$0"]"}'

["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]

Выполняет ту же работу, что и первая версия, первая awkразделяет поля, как и раньше, sedтеряет лишнее [и ], а последняя awkперекомпоновывает элементы, переопределяя RS, FSиOFS

4
18.03.2021, 23:26

Сperl:

perl -pe '
   s{([^]["]+|"[^"]*")|\[(?0)*\]}
    {$1 // "[". ($& =~ s/("[^"]*"|[^]["]+)|./$1/gr). "]"}ge'

Это использует рекурсивное регулярное выражение perl.

Внешний s{regex}{replacement-code}geтокенизирует ввод в один из:

  • любая последовательность символов, отличная от [, ]или"
  • строка в кавычках
  • группа [...](использует рекурсию в регулярном выражении для поиска соответствия])

Затем мы заменяем этот токен самим собой, если он находится в первых двух категориях ($1), а если нет, токен с не -заключенным в кавычки [, ], удаленным с использованием того же метода токенизации во внутреннем замена.

Чтобы обрабатывать экранированные кавычки и обратную косую черту внутри кавычек (, например "foo\"bar\\"), замените [^"]на (?:[^\\"]|\\.).

Сsed

Если ваш sedподдерживает опции -Eили -rдля работы с расширенными регулярными выражениями вместо базовых , вы можете сделать это с помощью цикла,замена самого внутреннего [...]на первый:

LC_ALL=C sed -E '
  :1
  s/^(("[^"]*"|[^"])*\[("[^"]*"|[^]"])*)\[(("[^"]*"|[^]["])*)\]/\1\4/
  t1'

(использование LC_ALL=Cдля ускорения и эквивалентности perl, который также игнорирует языковой стандарт пользователя, когда дело доходит до интерпретации байтов как символов ).

С точки зрения POSIX, это все еще должно быть выполнимо с чем-то вроде:

LC_ALL=C sed '
  :1
  s/^\(\(\("[^"]*"\)*[^"]*\)*\[\(\("[^"]*"\)*[^]"]*\)*\)\[\(\(\("[^"]*"\)*[^]["]*\)*\)\]/\1\6/
  t1'

Здесь используется \(\(a\)*\(b\)*\)*вместо (a|b)*, так как базовые регулярные выражения не имеют оператора чередования (BRE некоторых sedреализаций имеют \|для этого, но это не POSIX/portable ).

8
18.03.2021, 23:26

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

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

После захвата фрагмента удалите знаки квадратных скобок с нечетных частей фрагмента. Нечетная часть — это часть слева от двойной кавычки.

perl -lpe '
my $re;
$re = qr{\[(?:
      (?>"[^"]*") |
      (?>[^]"[]+) |
      (??{ $re }) 
    )*]}x;
s/$re/
  local $_ = $&;
  "[". 
  s{([^"]*")([^"]*")|([^"]+$)}{
    $1 =~ tr:[]::dr 
         . $2. 
    $3 =~ tr:[]::dr 
  }xger
 . "]"
/xge;
'

выход:

["q", "0", "[", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]
2
18.03.2021, 23:26

Ниже приведен еще один способ сгладить скобки, на этот раз с использованием нерекурсивного регулярного выражения.

echo "....." |\
perl -lne '
  my $re = qr{\[ (?{ local $a = 1 })
    (?:(?(?{ ! $a })(?!))
       (?:
         \[(?{ local $a=$a+1 }) |
         \](?{ local $a=$a-1 }) |
         (?>"[^"]*") |
         (?>[^]"[]+) 
      )
    )+
  (?(?{ $a })(?!))
}x;
print s/($re)/"[". 
  $1 =~ s{([^"]*(?:"|$))}{
    $|-- ? $1 : $1 =~ tr:][::dr;
  }regx
. "]"/regx;
' 
2
18.03.2021, 23:26

Мне было интересно, можно ли написать рекурсивные лямбда-выражения на Python, так что вот, пожалуйста:

echo '[[["q", "0"], "R"], "L"], ["q", [["1", "["], "]"]], [["q", ["2", "L"]], "R"], ["q", ["3", ["R", "L"]]]' | python -c 'import sys, ast; print([(lambda f: f(f))(lambda f, i=top_level_list: [e for a in i for e in (f(f,a) if isinstance(a, (tuple, list)) else (a,))]) for top_level_list in ast.literal_eval(sys.stdin.read())])'

[['q', '0', 'R', 'L'], ['q', '1', '[', ']'], ['q', '2', 'L', 'R'], ['q', '3', 'R', 'L']]

[Замените echoна ваш стандартный вывод.] Un -один -подстроен, Python выглядит как:

my_list = [[["q", "0"], "R"], "L"], ["q", [["1", "["], "]"]], [["q", ["2", "L"]], "R"], ["q", ["3", ["R", "L"]]]
[(lambda f: f(f))(lambda f, i=top_level_list: [e for a in i
                                                 for e in (f(f,a)
                                                 if isinstance(a, (tuple, list)) else (a,))
                                                 ])
                                                 for top_level_list in my_list]

Код выравнивания списка является рекурсивным, если элемент является кортежем/списком, в противном случае он создает значение. рекурсивный лямбда-код позволяет лямбде вызывать себя без имени. Остальная часть кода просто обрабатывает чтение mylistсо стандартного ввода.

Предупреждение :, если вы когда-нибудь запустите это в производство, ожидайте разговора -.

4
18.03.2021, 23:26

Это можно сделать с помощью sed:

sed -E ':a;s/(\[[^][]*)\[([^][]*)\]([^][]*\])/\1\2\3/;ta'

Идея состоит в том, чтобы сопоставить пару [ ]внутри нее, сопоставить пару, чтобы удалить [ ], которая, в свою очередь, не содержит [или ]. Чтобы избежать совпадения одного [или одного ], нам нужно использовать [^][]*. Что повторяется в нескольких местах:

  • (\[[^][]*)Сопоставьте (и захватите )один [, за которым следуют несколько не [или ].
  • \[и один[
  • ([^][]*)с последующим совпадением и захватом нескольких не [или ].
  • \]и один]
  • ([^][]*\]), за которыми следуют несколько не [или ], которые заканчиваются на ].

Затем замените весь захват на \1\2\3, который удалит внутреннюю пару [].

Окружите все вышеперечисленное меткой :aи циклом, если было сделано изменение taи замена повторяется до тех пор, пока больше не будут найдены внутренние []пары (и заменены ).

3
18.03.2021, 23:26

Теги

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