AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 757762
Accepted
Kamil
Kamil
Asked: 2023-09-30 00:06:55 +0800 CST2023-09-30 00:06:55 +0800 CST 2023-09-30 00:06:55 +0800 CST

Perl: mudança na interpretação dos símbolos de shell na invocação de system()?

  • 772

Hoje notei que algo mudou no Perl, provavelmente recentemente, na forma como ele executa comandos shell. Alguém poderia explicar o que mudou? Não consigo encontrar a resposta sozinho e, infelizmente, aprendemos sobre essa mudança da maneira mais difícil possível. Alguns novos usuários obtiveram conteúdos interessantes de seus novos diretórios pessoais...

Estou executando um comando/script simples:

#!/usr/bin/perl -w

system("ls -R /etc/skel/.[^.]*");

No Debian 11: perl v5.32.1, a saída é apenas o conteúdo de /etc/skel(como esperado):

.  ..  .bash_logout  .bashrc  .face  .face.icon  .kshrc  .profile

Mas no Debian 12: perl v5.36.0o globbing ^é ignorado e o todo /etcé lido, o que significa ..que não é ignorado .

Quando mudo ^para o símbolo alternativo !: system("ls -R /etc/skel/.[!.]*");, ele funciona conforme o esperado novamente.

A questão é: o que mudou no Perl no tratamento de símbolos !e ^na system()invocação?

EDITAR: 29.09.2023, 19h50

Fiz alguns testes nos dois servidores e parece que algo mudou dash?

Debian 11: dash Version: 0.5.11+git20200708+dd9ef66-5(não vejo uma --versionbandeira no traço, então é do APT).

root@s:~# dash -c 'ls -R /etc/skel/.[^.]*'
/etc/skel/.bash_logout  /etc/skel/.bashrc  /etc/skel/.forward+spam  /etc/skel/.kshrc  /etc/skel/.profile
root@s:~# dash -c 'ls -R /etc/skel/.[!.]*'
/etc/skel/.bash_logout  /etc/skel/.bashrc  /etc/skel/.forward+spam  /etc/skel/.kshrc  /etc/skel/.profile

Debian 12:dash Version: 0.5.12-2

[students] ~ ➽ $ dash -c 'ls -R /etc/skel/.[^.]*' | more
/etc/skel/..:
a2ps.cfg
a2ps-site.cfg
adduser.conf
adjtime
aliases
aliases.db
alsa
alternatives

[students] ~ ➽ $ dash -c 'ls -R /etc/skel/.[!.]*'
/etc/skel/.bash_logout  /etc/skel/.bashrc  /etc/skel/.face  /etc/skel/.face.icon  /etc/skel/.kshrc  /etc/skel/.profile

Atenciosamente, Kamil

perl
  • 3 3 respostas
  • 654 Views

3 respostas

  • Voted
  1. terdon
    2023-09-30T00:30:31+08:002023-09-30T00:30:31+08:00

    Não foi o Perl que mudou, é o shell padrão do seu sistema. A chamada do Perl system()usa /bin/sh. Em sistemas Debian recentes e derivados do Debian, esse é um link simbólico para dash, um shell POSIX básico. Nos sistemas mais antigos, e em muitos sistemas não-debian, é, em vez disso, um link simbólico para bash.

    E, de fato, os dois shells se comportam de maneira diferente com [^.]:

    $ dash -c 'ls -R /etc/skel/.[^.]*' 2>/dev/null | wc
       2875    2572   45543
    $ bash -c 'ls -R /etc/skel/.[^.]*' 2>/dev/null | wc
          5       5     103
    

    Você também pode testar isso facilmente fazendo:

    $ cd /bin
    $ sudo rm sh
    $ sudo ln -s bash sh
    

    E então execute seu script Perl novamente. Você verá que ele se comporta conforme o esperado. Apenas lembre-se de voltar e desfazer a alteração:

    $ cd /bin
    $ sudo rm sh
    $ sudo ln -s dash sh
    
    • 12
  2. Best Answer
    Stéphane Chazelas
    2023-09-30T02:06:52+08:002023-09-30T02:06:52+08:00

    A documentação da função perlde system()pode ser encontrada em perldoc -f system. Com perl 5.34, encontro:

    system LIST
    system PROGRAM LIST
    Faz exatamente a mesma coisa que exec, exceto que uma bifurcação é feita primeiro e o processo pai aguarda a saída do processo filho. Observe que o processamento de argumentos varia dependendo do número de argumentos. Se houver mais de um argumento em LIST, ou se LIST for um array com mais de um valor, inicia o programa dado pelo primeiro elemento da lista com argumentos dados pelo resto da lista. Se houver apenas um argumento escalar, o argumento será verificado quanto a metacaracteres do shell e, se houver algum, todo o argumento será passado para o shell de comando do sistema para análise (este é "/bin/sh -c" em plataformas Unix, mas varia em outras plataformas) . Se não houver metacaracteres de shell no argumento, ele será dividido em palavras e passado diretamente para "execvp", que é mais eficiente.

    Aqui, com system("ls -R /etc/skel/.[^.]*"), você está no caso em que:

    • um argumento é passado
    • o argumento contém metacaracteres de shell, ou seja, [e *¹ ( ^era um metacaractere no shell Bourne como um alias para |compatibilidade com versões anteriores do shell Thompson, mas não está mais no POSIX moderno sh).

    então será como se você tivesse escrito:

    system({"/bin/sh"} "sh", "-c", "ls -R /etc/skel/.[^.]*");
    

    Que pede shpara interpretar esse ls -R /etc/skel/.[^.]*código shell em um processo filho e aguarda seu encerramento.

    Exceto que ls -R /etc/skel/.[^.]*não é um código POSIX válido sh.

    Se você observar a especificação de Pathname Expansion que por sua vez se refere a Patterns Used for Filename Expansion na edição 2018 da especificação POSIX e, em particular, a parte sobre Patterns Matching a Single Character , você encontrará:

    [
    Se um colchete aberto introduzir uma expressão de colchetes como em XBD RE Bracket Expression, exceto que o caractere <exclamation-mark> ( '!' ) deve substituir o caractere <circumflex> ( '^' ) em sua função em uma lista não correspondente na notação de expressão regular , deve introduzir uma expressão de colchete padrão. Uma expressão entre colchetes começando com um caractere <circumflex> sem aspas produz resultados não especificados . Caso contrário, '[' deve corresponder ao próprio caractere.

    Em outras palavras, para negar um conjunto que você usa [!x], not [^x], e o que [^x]does não é especificado, ele pode corresponder ao mesmo que [!x]ou ^ou x(como seu sh) ou qualquer coisa no que diz respeito ao POSIX.

    Portanto, se o comportamento mudou para você, é provável que você shtenha mudado de alguém que se comportou de uma maneira nesse aspecto para outro que se comportou de outra maneira.

    No caso de dash(o shell usado no Debian, derivado do shpróprio NetBSD derivado do shell Almquist), há uma série de mudanças que afetam ou podem afetar o comportamento.

    • [EXPANDIR] Adicionadas opções configure --enable-glob e --enable-fnmatch (2007), que adicionou a possibilidade de compilar dashpara usar as libcs fnmatch()​​e glob()realizar globbing em vez de fazê-lo internamente ( dasho glob interno de 'não reconhece ^).
    • shell: Habilitar fnmatch/glob por padrão (maio de 2020): esse se tornou o padrão (e pode ou não suportar ^como um alias para !, o glibc oferece).
    • shell: Desative o glob novamente, pois ele remove as barras finais (novembro de 2020)
    • expandir: Sempre cite o acento circunflexo ao usar fnmatch (2022) após este relatório de bug . Aplica-se fnmatch()ao usado na caseinstrução, mas o uso de glob()ainda está desabilitado por padrão.

    Essa correção não é realmente relevante para o seu problema, mas observe que, por sua vez, ela introduz mais bugs como:

    $ string='\' pattern='[\^x]' dash -c 'case $string in ($pattern) echo match; esac'
    match
    

    Portanto, com o dash vinculado à GNU libc, houve uma pequena janela entre maio e novembro de 2020 onde ^teria sido reconhecido como um alias para !e seu 0.5.11+git20200708+dd9ef66-5 pousou no meio dele.

    A razão pela qual ^(de regexp) foi alterado para !in globs é histórica. Como visto acima ^(inicialmente esse caractere era uma seta para cima em ASCII, não um cursor) era um operador de pipe no shell Thompson e no shell Bourne, então echo [^x]teria sido o mesmo que echo [ | x]no sh.

    Esse ^alias |foi removido no shell Korn e o POSIX proíbe ^ser tratado como um canal, mas o shell Korn não mudou [!x]de volta para [^x]tentar preservar a compatibilidade com versões anteriores. Alguns outros shells, como bash ou zsh, fizeram (ou shells como csh que nunca tiveram uma bagagem de herança Bourne), então o POSIX deixa isso sem especificação.

    Então, seu código deveria ter sido:

    ls -R /etc/skel/.[!.]*
    

    Para ser shuma sintaxe válida. Agora ainda há mais problemas com esse código:

    • Suponho que a intenção seja listar os arquivos e diretórios ocultos (e seus conteúdos) diferentes de .e ..(que alguns shells ainda retornam em seus globos, embora isso quase nunca seja desejável), mas observe que faltam arquivos nomeados, ..foopor exemplo.
    • Se não houver nenhum arquivo correspondente, você receberá um erro informando que o arquivo chamado /etc/skel/.[^.]*não existe.

    perlé uma linguagem muito mais capaz do que she também é mais portátil, pois há apenas uma implementação; portanto, em vez de pedir shpara encontrar os arquivos ocultos para /etcpassar para ls, você pode fazê-lo em perl:

    @hidden_files = grep {!m{/\.\.?\z}} </etc/skel/.*>;
    if (@hidden_files) {
      system "ls", "-R", @hidden_files;
    }
    

    ¹ A rigor, o espaço também é um metacaractere em sh, mas não é considerado como tal nessa descrição do Perl; se não houver metacaracteres além do espaço, o perl faz a divisão do espaço sozinho, em vez de chamar sh.

    • 8
  3. u1686_grawity
    2023-09-30T00:35:45+08:002023-09-30T00:35:45+08:00

    Nada. Esses símbolos são interpretados pelo seu shell, não pelo Perl.

    O que system()acontece é gerar /bin/sh -ctoda a string de comando como argumento. O shell é o que interpreta todo o resto dentro dessa string – é por isso que é chamado de comando shell .

    Ao contrário das expressões regulares (regex), [^abc]não é realmente um elemento de sintaxe padrão em curingas de shell (globs) e é escrito da [!abc]maneira correta. Acontece que alguns shells, como o Bash, aceitam ambas as formas - mas não é garantido que /bin/sh seja Bash ou suporte qualquer extensão específica do Bash; ele só é necessário para suportar o que o POSIX exige de um shell.

    Portanto, no Debian, hoje em dia é muito mais provável que /bin/sh esteja vinculado ao dash, um shell muito mais minimalista (otimizado para desempenho), embora instalações antigas ainda possam tê-lo vinculado ao Bash, pois costumava ser o padrão muitos lançamentos atrás . Uma das diferenças é que o traço não suporta o ^símbolo alternativo, apenas !.

    (Também me lembro vagamente de algo do mês passado sobre até o Bash 5.2 se comportar da mesma maneira quando invocado o modo "shell POSIX"? Não consigo me lembrar agora.)


    Se eu pudesse acrescentar, essa não é realmente uma boa maneira de listar arquivos por meio do Perl. glob()Já tem sua própria função! E se você quiser que seja recursivo, use o File::Findmódulo padrão (ou faça uma função Perl recursiva). Mesmo com system(), findteria evitado esse problema, pois não precisa de ..exclusão.

    • 2

relate perguntas

  • erro zabbix_sender

  • atualizações cpanm vs apt

  • Correspondência de pares reversíveis em um arquivo CSV

  • Perl: é possível usar a substituição de variável?

  • calcule a soma de cada 2 linhas e substitua-as por outro valor se a soma for menor que um valor específico

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve