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 / 782873
Accepted
teeeeee
teeeeee
Asked: 2024-09-02 07:36:52 +0800 CST2024-09-02 07:36:52 +0800 CST 2024-09-02 07:36:52 +0800 CST

Script Bash que recebe vários argumentos de caminho e verifica se os arquivos podem ser criados com sucesso lá

  • 772

Gostaria de um script de shell bash (para meu sistema Debian 12) que aceitasse múltiplos caminhos como argumentos e, então, verificasse se um arquivo pode ser criado com sucesso em cada um deles. Então, o resultado deveria ser colocado no formato json.

O comando a ser executado deve ser:

./checkavailable.sh /the/first/path /the/second/path

(onde pode haver qualquer número de caminhos de entrada fornecidos, mas pelo menos um) e eu gostaria que a saída estivesse no seguinte formato:

[
  { "is_available": 0, "path": "/the/first/path/" },
  { "is_available": 1, "path": "/the/second/path/" }
]

Atualmente, consegui fazer isso com o seguinte script:

#!/bin/bash

# Check if first path is available
if touch $1/checkalivefile ; then
    avail_path1=1
    rm $1/checkalivefile
else
    avail_path1=0
fi

# Check if second path is available
if touch $2/checkalivefile ; then
    avail_path2=1
    $2/checkalivefile
else
    avail_path2=0
fi

echo "["
printf "  { \"is_available\": "$avail_path1", \"path\": \"$1\" } "
printf ",\n"
printf "  { \"is_available\": "$avail_path2", \"path\": \"$2\" } "
echo
echo "]"

mas é obviamente um pouco primitivo e é codificado para funcionar apenas com argumentos x2, e não é escalável para argumentos adicionais.

Eu poderia tentar adicionar um loop agora. No entanto, me deparei com este script, que parece fazer algo similar (para obter o uso do disco neste caso), mas usando awk em vez de um loop:

#!/bin/bash

echo "["
du -s -B1 "$@" | awk '{if (NR!=1) {printf ",\n"};printf "  { \"dir_size_bytes\": "$1", \"path\": \""$2"\" }";}'
echo
echo "]"

Alguém pode mostrar como essa abordagem usando "awk" pode ser aplicada ao meu caso? Sou muito novo em scripts bash, então espero aprender alguma coisa.

bash
  • 6 6 respostas
  • 402 Views

6 respostas

  • Voted
  1. Stéphane Chazelas
    2024-09-02T14:18:32+08:002024-09-02T14:18:32+08:00

    JSON é uma escolha ruim de formato, pois, quando codificado em UTF-8 (como é frequentemente exigido hoje em dia, e muitas ferramentas não oferecem suporte a JSON codificado não UTF-8), suas strings não podem conter caminhos de arquivo arbitrários (que são sequências arbitrárias de bytes não NUL).

    Mas se você estiver de acordo em produzir JSON não codificado em UTF-8, como muitas ferramentas no Linux lsfdfazem (mas que ferramentas como essa jqnão conseguem processar), você pode fazer:

    #! /bin/sh -
    exec perl -MJSON::PP -Mfiletest=access -e '
      print JSON::PP->new
                    ->indent
                    ->space_after
                    ->indent_length(2)
                    ->encode(
                      [
                        map {
                              "path" => $_,
                              "is_available" => !!(-d $_ && -x $_ && -w $_)
                            }, @ARGV
                      ]
                    )' -- "$@"
    

    Mas você também pode fazer um perlscript:

    #! /usr/bin/perl --
    use JSON::PP;
    use filetest 'access';
    print JSON::PP->new
                  ->indent
                  ->space_after
                  ->indent_length(2)
                  ->encode(
                    [
                      map {
                            'path' => $_,
                            'is_available' => !!(-d $_ && -x $_ && -w $_)
                          }, @ARGV
                    ]
                  );
    

    Observe que, em vez de tentar criar arquivos ali, o que é potencialmente destrutivo (e pode pelo menos atualizar o tempo de modificação desses diretórios), ele usa heurística (verifica se os caminhos identificam diretórios pesquisáveis ​​( -x) e weditáveis d).

    Isso ->indent->space_after->indent_length(2)é apenas para obter uma jqformatação bonita no estilo -. Você pode removê-lo para obter JSON compacto (como com jq -c).

    Substitua !!por intou, 0+por exemplo, se você quiser is_availableque seja um número 0/ 1em vez de um booleano false/ .true

    • 3
  2. Best Answer
    Stephen Harris
    2024-09-02T08:53:19+08:002024-09-02T08:53:19+08:00

    Seu script e o dfscript testam coisas diferentes. Um testa "posso escrever neste diretório" e o outro poderia testar "há espaço neste disco".

    Supondo que você queria o teste "posso escrever", seu script original está próximo, mas você precisa refatorar um pouco e fazer um loop na entrada.

    O grande problema com um loop simples como esse é remover o "," final, então colocamos isso em uma linha própria e então excluímos a última linha do loop. O JSON não se importa onde a vírgula está, contanto que ela esteja lá.

    Provavelmente touchtambém precisa ser jogado fora.

    Esta versão não foi testada...

    #!/bin/bash
    
    echo "["
    
    for p in $@
    do
      if touch "$p/checkalivefile" 2>/dev/null ; then
        avail_path=1
        rm "$p/checkalivefile"
      else
        avail_path=0
      fi
    
      printf "  { \"is_available\": "$avail_path", \"path\": \"$p\" }\n,\n"
    done | sed '$d'
    echo "]"
    
    • 2
  3. Jim L.
    2024-09-02T15:04:16+08:002024-09-02T15:04:16+08:00

    Muitos shells derivados de Bourne têm primitivos para verificar esse tipo de coisa, tornando touchdesnecessários os shells inferiores que têm um testutilitário semelhante a - com operadores -dand :-w

    $ mkdir dir{1..5} dir\ \"{6..10}\"
    $ chmod -w dir2 dir5 'dir "7"' 'dir "9"'; chmod -x dir\ \"8\"
    $ ls -ltrd dir*
    drwx--x---  2 jim wheel 2 Sep  2 00:01 dir1/
    dr-x--x---  2 jim wheel 2 Sep  2 00:01 dir2/
    drwx--x---  2 jim wheel 2 Sep  2 00:01 dir3/
    drwx--x---  2 jim wheel 2 Sep  2 00:01 dir4/
    dr-x--x---  2 jim wheel 2 Sep  2 00:01 dir5/
    drwx--x---  2 jim wheel 2 Sep  2 00:01 dir "6"/
    dr-x--x---  2 jim wheel 2 Sep  2 00:01 dir "7"/
    drw-------  2 jim wheel 2 Sep  2 00:01 dir "8"/
    dr-x--x---  2 jim wheel 2 Sep  2 00:01 dir "9"/
    drwx--x---  2 jim wheel 2 Sep  2 00:01 dir "10"/
    
    $  ./checkavailable.sh dir*
    [
      { "is_available": 1, "path": "dir\ \"10\"" },
      { "is_available": 1, "path": "dir\ \"6\"" },
      { "is_available": 0, "path": "dir\ \"7\"" },
      { "is_available": 0, "path": "dir\ \"8\"" },
      { "is_available": 0, "path": "dir\ \"9\"" },
      { "is_available": 1, "path": "dir1" },
      { "is_available": 0, "path": "dir2" },
      { "is_available": 1, "path": "dir3" },
      { "is_available": 1, "path": "dir4" },
      { "is_available": 0, "path": "dir5" }
    ]
    
    $ cat checkavailable.sh 
    #!/usr/bin/env bash
    
    declare -a avail_array
    
    readarray -t avail_array < <(
        while [[ $# -gt 0 ]]
        do
            # "!" because requested output is 1 for true, 0 for false
            ! ( [[ -d "$1" ]] && [[ -x "$1" ]] && [[ -w "$1" ]] )
            is_avail=$?
            printf '  { "is_available": %d, "path": "%q" },\n' $is_avail "$1"
            shift
        done
    )
    
    N=${#avail_array[@]}
    [[ $N -gt 0 ]] && {
        # Remove trailing comma from last entry:
        avail_array[$(($N-1))]="${avail_array[$(($N-1))]%,}"
        printf '[\n'
        printf '%s\n' "${avail_array[@]}"
        printf ']\n'
    }
    
    • 2
  4. Jetchisel
    2024-09-02T09:17:43+08:002024-09-02T09:17:43+08:00

    Usando matriz associativa de bash4+.

    #!/usr/bin/env bash
    
    declare -A paths ##: declare associative array
    
    for f; do  ##: Loop through all the give arguments
      [[ -z "$f" ]] && continue ##: If the given argument is two single '' quotes it breaks the array.
      if create_file=$(touch "$f"/checkalivefile 2>&1); then ##: command substitution in a variable has a useful exit status
        avail_path1=1
        rm -rf "$create_file"
      else
        avail_path1=0
      fi
      paths["$f"]="$avail_path1"
    done
    
    counter=1
    total="${#paths[@]}"
    
    ##: If no argument is given exit the script
    ((total)) || {
      printf >&2 'No directory given!\n'
      exit 1
    }
    
    printf '[\n'
    for i in "${!paths[@]}"; do
      case $counter in
        "$total") ##: Do not add a trailing `,` if it is the last element.
           printf '  { "is_available": %s, "path": "%s" }\n' "${paths[$i]}" "$i";;
        *)
         printf '  { "is_available": %s, "path": "%s" },\n' "${paths[$i]}" "$i";;
      esac
    ((counter++))
    done
    printf ']\n'
    

    • Veja help testpara verificar permissões. Como o que a outra resposta postada sugeriu. Em vez de touche rmPoderia ser algo como:
    for f; do
      [[ -z "$f" ]] && continue
      if [[ -d "$f" && -w "$f" && -x "$f" ]]; then
        avail_path1=1
      else
        avail_path1=0
      fi
      paths["$f"]="$avail_path1"
    done
    
    • Confira jq neste fórum também para processar/trabalhar com jsonestrutura de dados usando o shell.
    • 1
  5. kos
    2024-09-02T12:26:52+08:002024-09-02T12:26:52+08:00

    Como os caminhos UNIX podem muito bem conter aspas duplas e outros caracteres que, quando interpolados diretamente, quebrariam um arquivo JSON, seria mais seguro deixar um processador JSON cuidar da tarefa.

    O roteiro também poderia ser bastante simplificado.

    Eu faria algo assim:

    #!/usr/bin/env bash
    
    for p in "${@}"; do
            ! ([ -d "${p}" ] && [ -w "${p}" ] && [ -x "${p}" ])
            jq -n --argjson is_available "${?}" --arg path "${p}" '{ is_available: $is_available, path: $path }'
    done | jq -s
    

    Se for garantido que os caminhos não contêm sequências UTF-8 inválidas; caso contrário, como Stéphane Chazelas observa, algum tipo de codificação é necessária para representar corretamente os caminhos usando JSON; veja a resposta de Stéphane Chazelas para um método que lida com caminhos que contêm sequências UTF-8 inválidas.

    Para cada parâmetro $p, ele testará se o argumento não é um diretório gravável e pesquisável, de modo que $?conterá 0se o diretório não é gravável e pesquisável / 1se o diretório não é gravável e pesquisável;

    Em seguida, ele alimentará um argumento nomeado is_availablecom um valor de $?e um argumento nomeado pathcom um valor de $ppara jq, que produzirá um objeto JSON (bem formado) contendo os dados;

    Por fim, ele alimentará o fluxo de objetos para jq -sredução em uma matriz.

    % ls -la
    totale 4
    drwxr-xr-x 1 dev users  92  2 set 06.24  .
    drwxr-xr-x 1 dev users 518  2 set 06.24  ..
    drwxr-xr-x 1 dev users   0  2 set 05.37 'dir_with'$'\n''newline'
    drwxr-xr-x 1 dev users   0  2 set 06.10 'dir_with"double_quote'
    -rwx------ 1 dev users 183  2 set 06.24  script.sh
    % ./script.sh ~ dir_with$'\n'newline dir_with\"double_quote /root
    [
      {
        "is_available": 1,
        "path": "/home/dev"
      },
      {
        "is_available": 1,
        "path": "dir_with\nnewline"
      },
      {
        "is_available": 1,
        "path": "dir_with\"double_quote"
      },
      {
        "is_available": 0,
        "path": "/root"
      }
    ]
    

    Observe que isso is_availabledeve ser representado como um booleano, caso contrário, o que estiver analisando o JSON pode ter que testar a igualdade numérica em vez da veracidade:

    #!/usr/bin/env bash
    
    for p in "${@}"; do
            ! ([ -d "${p}" ] && [ -w "${p}" ] && [ -x "${p}" ])
            jq -n --argjson is_available "${?}" --arg path "${p}" '{ is_available: ($is_available==1), path: $path }'
    done | jq -s
    
    % ./script.sh ~ /root
    [
      {
        "is_available": true,
        "path": "/home/user"
      },
      {
        "is_available": false,
        "path": "/root"
      }
    ]
    
    • 1
  6. rr0ss0rr
    2024-09-02T10:09:15+08:002024-09-02T10:09:15+08:00

    Em vez de tocar, eu testaria para ver se o caminho é gravável [[ -w "$p" ]]. Para testar especificamente o diretório e gravável, então [[ -d "$p" && -w "$p" ]]. Também recomendo jo para lidar com a criação de quaisquer objetos json. Isso torna tudo muito fácil

    • 0

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

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