Pytanie Czytaj plik według bajtów w BASH


Muszę przeczytać pierwszy bajt podanego pliku, następnie drugi bajt, trzeci i tak dalej. Jak mogłem to zrobić w BASH? P.S Potrzebuję uzyskać HEX tych bajtów


11
2017-12-15 05:36


pochodzenie


potrzebujesz HEX-a od każdego bajtu? - Grijesh Chauhan
Bin i hex dla każdego bajtu pliku - michaeluskov
nadzbiór, losowy dostęp: stackoverflow.com/questions/1423346/... - Ciro Santilli 新疆改造中心 六四事件 法轮功


Odpowiedzi:


Nowy post we wrześniu 2016 roku!

Ponieważ jest to bardzo szczegółowe, ten dodatek zostanie przedstawiony na samym dole.

Aktualizacja do dodania konkretnej wersji bash (z bashisms)

Z nową wersją printf wbudowane, możesz zrobić dużo bez konieczności rozwidlania ($(...)) dzięki czemu twój skrypt jest o wiele szybszy.

Najpierw zobacz (przy użyciu seq i sed) jak analizować wyjście hd:

echo ;sed <(seq -f %02g 0 $[COLUMNS-1]) -ne '
    /0$/{s/^\(.*\)0$/\o0337\o033[A\1\o03380/;H;};
    /[1-9]$/{s/^.*\(.\)/\1/;H};
    ${x;s/\n//g;p}';hd < <(echo Hello good world!)
0         1         2         3         4         5         6         7
012345678901234567890123456789012345678901234567890123456789012345678901234567
00000000  48 65 6c 6c 6f 20 67 6f  6f 64 20 77 6f 72 6c 64  |Hello good world|
00000010  21 0a                                             |!.|
00000012

Czy szesnastkowa część zaczynała się od kolumny 10 i kończyła na kol. 56, oddzielona trzema znakami i mająca dodatkową przestrzeń w kolumnie 34.

Parsowanie tego może być wykonane przez:

while read line ;do
    for x in ${line:10:48};do
        printf -v x \\%o 0x$x
        printf $x
      done
  done < <( ls -l --color | hd )

Stary oryginalny post

Edytuj 2 w przypadku szesnastkowego, możesz użyć hd

echo Hello world | hd
00000000  48 65 6c 6c 6f 20 77 6f  72 6c 64 0a              |Hello world.|

lub od

echo Hello world | od -t x1 -t c
0000000  48  65  6c  6c  6f  20  77  6f  72  6c  64  0a
          H   e   l   l   o       w   o   r   l   d  \n

wkrótce

while IFS= read -r -n1 car;do [ "$car" ] && echo -n "$car" || echo ; done

Spróbuj ich:

while IFS= read -rn1 c;do [ "$c" ]&&echo -n "$c"||echo;done < <(ls -l --color)

Wyjaśniać:

while IFS= read -rn1 car  # unset InputFieldSeparator so read every chars
    do [ "$car" ] &&      # Test if there is ``something''?
        echo -n "$car" || # then echo them
        echo              # Else, there is an end-of-line, so print one
  done

Edytować; Pytanie zostało poddane edycji: potrzebujesz wartości szesnastkowych !?

od -An -t x1 | while read line;do for char in $line;do echo $char;done ;done

Próbny:

od -An -t x1 < <(ls -l --color ) |        # Translate binary to 1 byte hex 
    while read line;do                    # Read line of HEX pairs
        for char in $line;do              # For each pair
            printf "\x$char"              # Print translate HEX to binary
      done
  done

Demo 2: Mamy zarówno szesnastkowy, jak i binarny

od -An -t x1 < <(ls -l --color ) |        # Translate binary to 1 byte hex 
    while read line;do                    # Read line of HEX pairs
        for char in $line;do              # For each pair
            bin="$(printf "\x$char")"     # translate HEX to binary
            dec=$(printf "%d" 0x$char)    # translate to decimal
            [ $dec -lt 32  ] ||           # if caracter not printable
            ( [ $dec -gt 128 ] &&         # change bin to a single dot.
              [ $dec -lt 160 ] ) && bin="."
            str="$str$bin" 
            echo -n $char \               # Print HEX value and a space
            ((i++))                       # count printed values
            if [ $i -gt 15 ] ;then
                i=0
                echo "  -  $str"
                str=""
              fi
      done
  done

Nowy post we wrześniu 2016 r .:

Może to być przydatne w bardzo szczególnych przypadkach (użyłem ich do ręcznego kopiowania partycji GPT między dwoma dyskami, na niskim poziomie, bez konieczności /usr zamontowane...)

Tak, bash może czytać pliki binarne!

... ale tylko jeden bajt, po jednym ... (ponieważ `char (0) 'nie może być poprawnie odczytany, jedynym sposobem na ich prawidłowe odczytanie jest rozważenie koniec pliku, gdzie jeśli nie jest odczytany żaden znak i koniec pliku nie został osiągnięty, to odczyt znaku jest char (0)).

Jest to bardziej dowód koncepcji niż rzetelne narzędzie: istnieje czysty  wersja hd (hexdump).

To ostatnie użycie baszty, pod bash v4.3 lub wyżej.

#!/bin/bash

printf -v ascii \\%o {32..126}
printf -v ascii "$ascii"

printf -v cntrl %-20sE abtnvfr

values=()
todisplay=
address=0
printf -v fmt8 %8s
fmt8=${fmt8// / %02x}

while LANG=C IFS= read -r -d '' -n 1 char ;do
    if [ "$char" ] ;then
        printf -v char "%q" "$char"
        ((${#char}==1)) && todisplay+=$char || todisplay+=.
        case ${#char} in
         1|2 ) char=${ascii%$char*};values+=($((${#char}+32)));;
           7 ) char=${char#*\'\\};values+=($((8#${char%\'})));;
           5 ) char=${char#*\'\\};char=${cntrl%${char%\'}*};
                values+=($((${#char}+7)));;
           * ) echo >&2 ERROR: $char;;
        esac
      else
        values+=(0)
      fi

 

    if [ ${#values[@]} -gt 15 ] ;then
        printf "%08x $fmt8 $fmt8  |%s|\n" $address ${values[@]} "$todisplay"
        ((address+=16))
        values=() todisplay=
      fi
  done

if [ "$values" ] ;then
        ((${#values[@]}>8))&&fmt="$fmt8 ${fmt8:0:(${#values[@]}%8)*5}"||
            fmt="${fmt8:0:${#values[@]}*5}"
        printf "%08x $fmt%$((
                50-${#values[@]}*3-(${#values[@]}>8?1:0)
            ))s |%s|\n" $address ${values[@]} ''""'' "$todisplay"
fi
printf "%08x (%d chars read.)\n" $((address+${#values[@]})){,}

Możesz spróbować / użyć tego, ale nie próbuj porównywać występów!

time hd < <(seq 1 10000|gzip)|wc
   1415   25480  111711
real    0m0.020s
user    0m0.008s
sys     0m0.000s

time ./hex.sh < <(seq 1 10000|gzip)|wc
   1415   25452  111669
real    0m2.636s
user    0m2.496s
sys     0m0.048s

ta sama praca: 20 ms dla hd vs 2000ms dla mojego bash script.

... ale jeśli chcesz przeczytać 4 bajty w nagłówku pliku lub nawet adres sektora na twardym dysku może to zrobić ...


14
2017-12-15 07:30





Próbowałeś xxd? Daje bezpośrednio zrzut heksadecymalny, jak chcesz ...

W twoim przypadku komenda brzmi:

xxd -c 1 /path/to/input_file | while read offset hex char; do
  #Do something with $hex
done

Uwaga: wyodrębnij znak z heksadecymali, a nie podczas czytania linii. Jest to wymagane, ponieważ odczyt nie przechwytuje odpowiednio białej przestrzeni.


4
2017-12-15 08:23





za pomocą read pojedynczy znak może być odczytywany na raz w następujący sposób:

read -n 1 c
echo $c   

[ODPOWIEDŹ]

Spróbuj tego:

#!/bin/bash
# data file
INPUT=/path/to/input.txt

# while loop
while IFS= read -r -n1 char
do
        # display one character at a time
    echo  "$char"
done < "$INPUT"

Od tego połączyć 


Druga metoda, Za pomocą awk, przejdź przez char przez char

awk '{for(i=1;i<=length;i++) print substr($0, i, 1)}' /home/cscape/Desktop/table2.sql 


trzecia droga,

$ fold -1 /home/cscape/Desktop/table.sql  | awk '{print $0}'

EDYTOWAĆ: Aby wydrukować każdy znak jako HEX numer:

Załóżmy, że mam nazwę pliku file :

$ cat file
123A3445F 

Napisałem awk skrypt (named x.awk) do tego odczytywać char z file i wydrukuj w HEX :

$ cat x.awk
#!/bin/awk -f

BEGIN    { _ord_init() }

function _ord_init(    low, high, i, t)
{
    low = sprintf("%c", 7) # BEL is ascii 7
    if (low == "\a") {    # regular ascii
        low = 0
        high = 127
    } else if (sprintf("%c", 128 + 7) == "\a") {
        # ascii, mark parity
        low = 128
        high = 255
    } else {        # ebcdic(!)
        low = 0
        high = 255
    }

    for (i = low; i <= high; i++) {
        t = sprintf("%c", i)
        _ord_[t] = i
    }
}
function ord(str,    c)
{
    # only first character is of interest
    c = substr(str, 1, 1)
    return _ord_[c]
}

function chr(c)
{
    # force c to be numeric by adding 0
    return sprintf("%c", c + 0)
}

{ x=$0; printf("%s , %x\n",$0, ord(x) )} 

Aby napisać ten skrypt użyłem awk-documentation
Teraz możesz tego użyć awk skrypt do swojej pracy w następujący sposób:

$ fold -1 /home/cscape/Desktop/file  | awk -f x.awk
1 , 31
2 , 32
3 , 33
A , 41
3 , 33
4 , 34
4 , 34
5 , 35
F , 46

UWAGA: A wartosc jest 41 w dziesiętnym HEX. Aby drukować w formacie dziesiętnym %xdo %d w ostatniej linii skryptu x.awk.

Spróbuj!!


4
2017-12-15 05:45



Aby wydrukować w systemie dziesiętnym, użyj ostatniego wiersza { x=$0; printf("%s , %d\n",$0, ord(x) )} - Grijesh Chauhan
możesz także użyć echo "$char" | hexdump w moim pierwszym przykładzie - Grijesh Chauhan


Jeszcze inne rozwiązanie, używając głowy, ogona i printf:

for a in $( seq $( cat file.txt | wc -c ) ) ; do cat file.txt | head -c$a | tail -c1 | xargs -0 -I{} printf '%s %0X\n' {} "'{}" ; done

Bardziej czytelny:

#!/bin/bash

function usage() {
    echo "Need file with size > 0"
    exit 1
}

test -s "$1" || usage

for a in $( seq $( cat $1 | wc -c ) )
do
    cat $1 | head -c$a | tail -c1 | \
    xargs -0 -I{} printf '%c %#02x\n' {} "'{}"
done

1
2017-12-16 11:16





Chociaż chciałem raczej rozwinąć własną pocztę Perleone (tak jak było jego podstawowa koncepcja!), moja edycja została ostatecznie odrzucona i uprzejmie doradzono, że powinna ona zostać opublikowana jako osobna odpowiedź. Wystarczająco uczciwe, więc to zrobię.

W skrócie rozważamy ulepszenia oryginalnego skryptu Perleone:

  • seq Byłoby to całkowicie przesadą. Prosty while pętla z a używany jako (podobnie jak prosta) zmienna licznika wykona zadanie dobrze (i znacznie szybciej)
  • Maksymalna wartość, $(cat $1 | wc -c)  musi być przypisane do zmiennej, w przeciwnym razie zostanie ona ponownie obliczona za każdym razem i sprawi, że ten alternatywny skrypt będzie działać wolniej niż ten, z którego został wyprowadzony.
  • Nie ma potrzeby marnowania funkcji na prostej linii informacji o użytkowaniu. Konieczne jest jednak wiedzieć o (obowiązkowym) nawiasach klamrowych wokół dwóch poleceń, ponieważ bez { }, exit 1 Polecenie zostanie wykonane w obu przypadkach, a interpreter skryptu nigdy nie wykona go w pętli. (Ostatnia uwaga: ( ) też będzie działać, ale nie w taki sam sposób! Nawiasy odradzają się podpowłoki, podczas gdy nawiasy klamrowe wykonają polecenia wewnątrz nich w obecny muszla.)
#!/bin/bash

test -s "$1" || { echo "Need a file with size greater than 0!"; exit 1; }

a=0
max=$(cat $1 | wc -c)
while [[ $((++a)) -lt $max ]]; do
  cat $1 | head -c$a | tail -c1 | \
  xargs -0 -I{} printf '%c %#02x\n' {} "'{}"
done

0
2017-12-02 01:56





posługiwać się read z -n opcja.

while read -n 1 ch; do
  echo $ch
done < moemoe.txt

-1
2017-12-15 05:42



To nie czyta poprawnie spacji. - John Kugelman
Czyta to \ n jako pusty ciąg - michaeluskov


Mam sugestię, ale chciałabym otrzymać od wszystkich opinie i osobistą poradę od użytkownika syntaxerror.

Nie wiem zbyt wiele o bashu, ale pomyślałem, że może lepiej będzie mieć "cat $ 1" przechowywany w zmiennej ... ale problem polega na tym, że polecenie echo również przyniesie niewielki narzut w prawo?

test -s "$1" || (echo "Need a file with size greater than 0!"; exit 1)
a=0
rfile=$(cat $1)
max=$(echo $rfile | wc -c)
while [[ $((++a)) -lt $max ]]; do
  echo $rfile | head -c$a | tail -c1 | \
  xargs -0 -I{} printf '%c %#02x\n' {} "'{}"
done

moim zdaniem miałoby to lepsze wyniki, ale nie perfekcyjnie testowałem ...


-1
2018-01-01 16:39