POSIX shell funkcia: tlač argumenty ako TSV záznam

0

Otázka

Píšem POSIX shell funkciu, ktorá vypíše jeho argumenty ako TSV záznam.
Každý argument je utiekol s týmito pravidlami:

  • \n pre newline
  • \t na karte
  • \r pre carriage return
  • \\ pre spätné lomítka

Je tu funkcia:

#!/bin/sh

tsv_print() {
    rec=
    for str in "$@"
    do
        esc=
        i=${#str}
        until [ $i -eq 0 ]
        do
            end="${str#?}"
            chr="${str%"$end"}"

            case $chr in
            "$__TAB__") chr='\t' ;;
            "$__LF__") chr='\n' ;;
            "$__CR__") chr='\r' ;;
            \\) chr='\\' ;;
            esac

            esc="$esc$chr"
            str="${end}"
            i=$((i-1))
        done
        rec="$rec${rec:+"$__TAB__"}$esc"
    done
#   echo "$rec"
    printf '%s\n' "$rec"
}

S — bolestivé, aby si v kód — znaky uložené vopred a dopĺňa takto:

__TAB__=$(printf '\t')
__CR__=$(printf '\r')
__LF__="
"

Ja by som chcel vedieť:

  1. Prečo nie je môj kód uniknúť znakov na všetkých?

    edit: Ako @GordonDavisson poukázal, echo bola vinníka!! Pomocou printf zdá sa, že iba prenosné spôsobom, s nákladmi možné vidlice.

  2. Je tam lepšie, špecifikácii POSIX, spôsob ako to urobiť? awk a sed nezdá vhodné pre prácu...

  3. Ako by ste robiť osn úteku?

    edit: Ako @KamilCuk publikované v jeho odpoveď, a printf '%b' by stačiť; v TSV záznam má správny formát, pre ktoré.


postscript

Na konci, funkcia nebol potrebný, pretože vstup nemal obsahovať ľubovoľný znak escape. Povedal, že, vstupný formát nebolo, že rovno-dopredu, ktorú chcete previesť. Bol to STAR Súbor s rôznou počet stĺpcov na riadok (obmedzenie linky na 80 znakov max) a obsahujú kótované reťazce...

vstup:

...
loop_ 
 _refl_0201 _refl_0012 _refl_2003 _refl_1600 _refl_1304 _refl_1305 _refl_1800
 _refl_1801 _refl_1802 _refl_1803 _refl_1804 _refl_1805 _refl_1806 _refl_1701
 _refl_1700 _refl_1202
'0 0 6' .147364 Z000020c1 .41 1 78.45 3.501 35.2221 -35.2221 0 -1.6055 -3.0963
-36.7288 -5.0964 39.3109 5.909983 '0 0 12' .294551 Z000010c1 .9 1 48.44 2.3805
39.910008 39.9101 .268379-04 1.75598 3.09745 41.6656 3.09809 47.8384 0 .939517
...

výstup (separátory sú karty):

_refl_0201 _refl_0012 _refl_2003 _refl_1600 _refl_1304 _refl_1305 _refl_1800 _refl_1801 _refl_1802 _refl_1803 _refl_1804 _refl_1805 _refl_1806 _refl_1701 _refl_1700 _refl_1202
'0 0 8' .147364 Z000020c1 .41 1 78.45 3.501 35.2221 -35.2221 0 -1.6055 -3.0963 -36.7288 -5.0964 39.3109 5.909983
'0 0 14' .294551 Z000010c1 .9 1 48.44 2.3805 39.910008 39.9101 .268379-04 1.75598 3.09745 41.6656 3.09809 47.8384 0.939517
...
escaping posix sh
2021-11-20 18:22:21
2
1

Je tam lepšie, špecifikácii POSIX, spôsob ako to urobiť?

Ja si nemyslím, vaša metóda je v poriadku, a bude neuveriteľne pomaly.

Ak je "lepšie" je rýchla, vždy sa môžete napísať POSIX-kompatibilné C program. (Ale naozaj, stačí kompilácie GNU sed a potom sed -z to).

Chcel by som ísť s awk -v FS='' -v RS='' '{ gsub(/\\/, "\\\\"); gsub("\r", "\\r"); gsub(/\t/, "\\t"); gsub(/\n/, "\\n")} 1'aj busybox awk rukoväte, že a napíšte celú vec s awk.

Ako by ste robiť osn úteku?

printf "%b"


__LF__="
"
__TAB__=$(printf '\t')
__CR__=$(printf '\r')
2021-11-20 22:30:54

Nemusíte printf vôbec pre newline. Stačí vložiť newline v kótovaných reťazec.
chepner

Som možno písať C program, ak nie je kompilátor k dispozícii na stroji, ale To nemusí byť potrebné v prípade, ak údaje na prevod nie je príliš veľká
Fravadona

Dobre, môžete cross-compile to lokálne a potom ho skopírujte. TBH ak ste ísť touto cestou, cross-compile busybox, a potom budete mať všetky nástroje, a potom napísať to všetko v ispell.
KamilCuk

Vzhľadom na inout údajov, musel som napísať ispell program
Fravadona
1

Môžete to urobiť pomocou sed ale existuje niekoľko trikov, potrebný na to, aby to fungovalo.

Musíte escape "\" prvý, tak lomky z "\t", "\n" a "\r", nebudú unikol.

sed funguje na líniu, ale stačí, aby ho pridať "\n" na konci každého riadku, a odstrániť newline znaky so samostatným príkaz.

sed lieči posledný riadok string rovnaké bez ohľadu na to, či to končí s "\n" alebo nie, ale pridanie '.' na konci jeho textu, mazanie a 3 posledná znaky výstup robí trik.

Toto riešenie je oveľa rýchlejšie, než shell slučky. (V podstate všetko, čo je rýchlejšie, než sa shell.)

tsv_print() {
    is_first_arg='yes'
    while [ $# -ne 0 ]
    do
        test "$is_first_arg" = 'yes' && is_first_arg='no' || printf '\t'
        printf '%s.' "$1" \
        | sed -e 's/\\/\\\\/g' \
            -e 's/\t/\\t/g' \
            -e 's/$/\\n/' \
            -e 's/\r/\\r/g' \
        | tr -d '\n' \
        | head -c -3
        shift
    done
}

Btw, existujú jednoduchšie spôsoby, ako definovať tieto konštanty.

# StackOverflow replaces the characters but it works in a file.
__TAB__='   ' # insert <tab> here
__LF__='
' # insert \n here
__CR__='
' #insert \r here

...alebo

# This one works fine when copied from here.
__TAB__="$(printf '\t')"
__LF__='
'
__CR__="$(printf '\r')"
2021-11-20 21:20:31

Žiadne IFS triky sú potrebné pre váš posledný príklad. Ani karty, ani prepravu, vráti budú odstránené z výstupu printf príkaz nahradenia.
chepner

(V skutočnosti, ste globálne nastavenie hodnoty IFS v oboch prípadoch; úlohy sebou neberte odporúčame úlohy, a printf nevyzerá na hodnotu IFS v prostredí, rovnako.)
chepner

@chepner zdá Sa, že funguje bez IFS na mojom systéme. Neviem prečo som si myslel, že by pásy "\r", hoci, som zmätený o tabulátor. Nie je predvolená hodnota IFS "\t\n"?
NO_NAME

Žiadne slovo-rozdelenie je aplikovaný na pravej strane priradenia. Je to príkaz nahradenia sebe, že pásy koncové newlines, nezávisle od aktuálnej hodnoty IFS.
chepner

V iných jazykoch

Táto stránka je v iných jazykoch

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................