Como eu exibo nomes de arquivos que contêm dois caracteres e um deles é c?

4

Eu tentei fazer ls [a-z][a-z] , mas parece que não está funcionando.

    
por amendeep singh 05.10.2018 / 19:13

4 respostas

14

Com o bash, defina as configurações de glob para que as correspondências ausentes não acionem um erro:

shopt -u failglob  # avoid failure report (and discarding the whole line).
shopt -s nullglob  # remove (erase) non-matching globs.
ls ?c c?

O ponto de interrogação é um caractere glob que representa um único caractere. Como você deseja nomes de arquivos de dois caracteres, um deles deve ser um c e, portanto, é o primeiro caractere ou o último caractere.

Com shopt -s dotglob , isso também mostraria um arquivo chamado .c .

Se não houver arquivos correspondentes, a configuração dessas opções de shell faz com que todos os argumentos sejam removidos, resultando em um ls - listando qualquer coisa / tudo por padrão.

Use isso, em vez disso:

shopt -s nullglob  ## drop any missing globs
set -- ?c c?       ## populate the $@ array with (any) matches
if [ $# -gt 0 ]    ## if there are some, list them
  ls -d "$@"
fi
    
por 05.10.2018 / 19:20
10

Acho que a melhor maneira é usar find :

find . -type f -name '*c*' -name '??'

Isso será pesquisado recursivamente. Para listar apenas arquivos no diretório atual:

find . ! -name . -prune  -type f -name '*c*' -name '??'
    
por 05.10.2018 / 21:25
3

Em bash :

  • c seguido por um caractere ( ?c ) ou um caractere (incluindo . com dotglob ) seguido por c ( ?c ).

    shopt -s extglob failglob dotglob
    printf '%s\n' @(?c|c?)
    
  • Todos os arquivos, exceto aqueles que não contêm c ( !(*c*) ) ou que não são dois caracteres ( !(??) ). Aqui, usando uma negação dupla para conseguir a conjunção.

    shopt -s extglob failglob dotglob
    printf '%s\n' !(!(*c*)|!(??))
    

Em zsh :

  • printf '%s\n' (?c|c?)(D)

    (onde D habilita dotglob ; failglob (chamado nomatch in zsh ) está ativado por padrão).

  • negação dupla usando o operador ~ exceto e os operadores estendidos ^ negação:

    set -o extendedglob
    printf '%s\n' *c*~^??(D)
    

Em ksh93 :

  • não há nomatch / failglob em ksh93, mas você pode fazer isso manualmente:

    FIGNORE='@(.|..)' # only ignore . and .. and not the other dotfiles
    files=(~(N)@(?c|c?))
    if ((${#files[@]})); then
      printf '%s\n' "${files[@]}"
    else
      echo >&2 No match
      exit 1
    fi
    

    (onde ~(N) ativa nullglob para esse glob).

  • ksh93 tem um operador de conjunção. Então, aqui, você também pode alterar a linha files=(...) para:

    files=(~(N)@(*c*&??))
    
por 06.10.2018 / 23:18
3

Se os arquivos existirem (e não tiverem caracteres de escape como \c ), você poderá usar um glob:

echo ?c c?

que corresponderá aos arquivos com um caractere ( ? ) seguido por c ou que tenham c seguido por um caractere ( ? ).

Mas haverá falhas nos nomes de arquivo que começam com um traço como -n ou -e ou com caracteres de barra invertida como \c ou \n com echo como -e e -n são especiais para (alguns shells) echo e \c ou \n seriam interpretados como uma sequência de escape ( \c termina a saída e \n imprime uma nova linha, não os caracteres textuais \n em alguma implementação de shell do echo e com o bash quando a opção shopt -s xpg_echo está definida). Outros aplicativos ou utilitários (como ls ) terão algumas outras opções e podem falhar com muitos outros traços iniciados ou interpretar outros caracteres de escape em nomes de arquivos.

Também listará um arquivo chamado cc duas vezes.

  • Se um arquivo pode começar com um traço (como -n), use:

    $ ls -d -- ?n 
    -n 
    

    Ou melhor:

    $ ls -d ./?n
    ./-n
    

Advertências

  • Glob match no file.

    1. Se os arquivos não existirem, o glob não será expandido e o glob será impresso em sua forma original.

      $ echo ?m m?
      ?m m?
      
    2. Exceto em zsh.

      $ zsh -c 'echo ?m m?'
      zsh:1: no matches found: ./m?
      

      O shell sairá com um erro.
      Esse comportamento é controlado pela opção Zsh's nomatch .

      $ zsh -c 'setopt +o nomatch; echo ?m m?'
      ?m m?
      

      Ou:

      $ zsh -c 'echo ?m(N) m?(N)'
      ?m m?
      

      Mas isso imprimirá mm duas vezes.

    3. No bash, você pode obter um resultado semelhante ao zsh se a opção do shell failglob estiver definida:

      $ bash -c 'shopt -s failglob; echo ?m m?'
      bash: no match: ?m
      

      Mas o script não irá parar, a shell não irá sair. Bem, tecnicamente, a linha em que o glob é usado não é mais executada, mas a execução é retomada na próxima linha de script.

    4. No bash você pode definir a opção nullglob para remover globs que não combinam:

      $ bash -c 'shopt -s nullglob; echo ?m m? done'
      done
      
  • Usando o ls (semelhante a outros programas)

    1. Com arquivos correspondentes, não haverá problema, mas também corresponderá aos diretórios (e lista):

      $ ls ?c c?
      bc  cz  sc
      
      ac:
      hjk
      

      Beter use -d com ls (os diretórios serão incluídos, mas não expandidos).

      $ ls -d ?c c?
      ac bc cz sc
      
    2. Globs não conseguindo corresponder a um arquivo (ou diretório)

      Em seguida, ls relatará uma falha

      $ ls ?m m?
      ls: cannot access '?m': No such file or directory
      

      Outros programas podem (provavelmente) fazer não fazer verificações semelhantes.

    3. Usar nullglob com bash fornecerá uma lista vazia para ls, portanto, listará o conteúdo ou o pwd como se apenas ls fosse executado:

      $ ls ?m m?
      ac a.out cz 3 b sc ab bc
      

      Isso pode ser evitado usando ./

      $ ls -d ./?m ./m?
      ls: cannot access './?m': No such file or directory
      ls: cannot access './m?': No such file or directory
      

Ou você pode usar um regex encontrado (mas ele irá percorrer subdiretórios com prune missing) (Note que isso não irá repetir um arquivo chamado cc):

    $ find . ! -name . -prune -regex '.*/\(.c\|c.\)'
    ./ac
    ./cc
    ./sc
    ./bc
    ./cz
    
por 06.10.2018 / 02:11