Suponha que haja algum texto de um arquivo:
(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
Eu quero adicionar 11 a cada número seguido por um "
em cada linha, se houver um, ou seja,
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)
Aqui está minha solução usando o GNU AWK e o regex:
awk -F'#' 'NF>1{gsub(/"(\d+)\""/, "\1+11\"")}'
ou seja, eu quero substituir (\d+)\"
por \1+10\"
, onde \1
está o grupo representando (\d+)
. Mas isso não funciona. Como posso fazer isso funcionar?
Se o gawk não é a melhor solução, o que mais pode ser usado?
Respostas:
Tente isso (é necessário um gawk).
Teste com seu exemplo:
Observe que este comando não funcionará se os dois números (por exemplo, 1 "e" # 1 ") forem diferentes ou se houver mais números na mesma linha com esse padrão (por exemplo, 23" ... 32 "..." # 123 ") em uma linha.
ATUALIZAR
Desde @Tim (OP) disse que o número seguido por
"
mesma linha poderia ser diferente, fiz algumas alterações na minha solução anterior e a fiz funcionar no seu novo exemplo.BTW, a partir do exemplo, sinto que poderia ser uma tabela de estrutura de conteúdo, então não vejo como os dois números podem ser diferentes. Primeiro seria o número da página impressa e o segundo com # seria o índice da página. Estou certo?
Enfim, você conhece melhor sua exigência. Agora a nova solução, ainda com gawk (eu quebro o comando em linhas para facilitar a leitura):
teste com seu novo exemplo:
EDIT2 com base no comentário de @Tim
Você está certo para o separador na parte de entrada e saída. Definiu separador como:
Existem duas aspas duplas, porque é mais fácil capturar os dois números que você deseja (com base em sua entrada de exemplo).
Exatamente!
Isto é de http://www.gnu.org/s/gawk/manual/html_node/String-Functions.html . você pode ler para obter um uso detalhado do gensub.
fonte
awk -F'#'
, parece que você deseja fazer a alteração somente após o '#'?FS=OFS="\" \"#"
significa que o separador de campo na entrada e na saída é aspas duplas, espaço, aspas duplas e #? por que especificar aspas duplas duas vezes? (2) in/.* ([0-9]+)$/
,$
significa o fim da string? (3) no terceiro argumento de gensub (), qual é a diferença entre"g"
e"G"
?Ao contrário de quase todas as ferramentas que fornecem substituições regexp, o awk não permite referências posteriores, como
\1
no texto de substituição. O GNU Awk dá acesso a grupos correspondentes se você usar amatch
função , mas não com~
ousub
ougsub
.Observe também que, mesmo se tiver
\1
sido suportado, seu snippet acrescentará a string+11
, não executará um cálculo numérico. Além disso, seu regexp não está certo, você está combinando coisas como essas"42""
e não"#42"
.Aqui está uma solução awk (aviso, não testado). Ele executa apenas uma única substituição por linha.
Seria mais simples no Perl.
fonte
awk
pode fazê-lo, mas não é direto, mesmo usando backreferencing.O GNU awk possui um registro posterior (parcial), na forma de gensub .
As instâncias de
123"
são temporariamente envolvidas\x01
e\x02
marcadas como não modificadas (parasub()
. CoOu você pode simplesmente passar pelo processo de mudança de candidatos à medida que avança; nesse caso, a referência posterior e os "colchetes" não são necessários; mas é necessário acompanhar o índice de caracteres.
Aqui está outra maneira, usando
gensub
e arraysplit
e\x01
como um delimitador de campo (para divisão ). \ X02 marca um elemento do array como candidato à adição aritmética.fonte
"\x01\\1\"\x02"
significa? Eu ainda não entendo\x01
e\x02
. (2) quão diferente é o retorno$0
degensub
e$0
como o último argumento paragensub
?\x01
e\x02
são usados como marcadores de substituição. Estes valores são altamente improvável que seja em qualquer normal de arquivo de texto, para que eles são igualmente "muito" seguro de usar (ie. Não encontrar um confronto com os pré-existentes) .. Eles são apenas rótulos temporários .. Re$0=gensub(... $0)
.. ver isso link Funções de manipulação de string , mas em resumo: (gensub) retorna a string modificada como resultado da função e a string de destino original não é alterada. ... A$0=
simplesmente modifica o alvo original ..Como as soluções em (g) awk parecem se tornar bastante complexas, eu queria adicionar uma solução alternativa no Perl:
Explicação:
-w
ativa avisos (que avisam sobre possíveis efeitos indesejados).-p
implica um loop em torno do código que funciona de forma semelhante a sed ou awk, salvando cada linha de entrada automaticamente na variável padrão$_
,.-e
diz ao perl que o código do programa está seguindo na linha de comando, não em um arquivo de script.s/.../.../
) em$_
, onde uma sequência de dígitos, se for seguida por a"
, será substituída pela sequência, interpretada como um número na adição, mais 11.(?=pattern)
procura a"
sem levá-la para a partida, portanto não precisamos repeti-la na substituição. A variável MATCH$&
na substituição conterá apenas o número./e
modificador para o regex dizperl
para "executar" a substituição como código em vez de tomá-la como uma string./g
modificador torna a substituição "global", repetindo-a em todas as correspondências da linha.$&
Infelizmente, a variável MATCH prejudicará o desempenho do código nas versões Perl anteriores à 5.20. Uma solução mais rápida (e não muito mais complexa) usaria o agrupamento e a referência anterior$1
:E se a afirmação antecipada parecer muito confusa, você também poderá substituir as aspas explicitamente:
fonte