Verwenden Sie die Prozessersetzung, um Programme auszutricksen, die Dateien erwarten, mit bestimmten Erweiterungen als Argument?

7

Hier ist mein Anwendungsfall: Das Befehlszeilenprogramm meltkann einen Dateinamen mit der Erweiterung .meltin der Befehlszeile akzeptieren und öffnen. Als Beispiel ist dies eine richtige test_p.meltDatei:

colour:blue
out=24

colour:red
out=48

... die öffnet und spielt mit melt test_p.melt.

Die Sache ist nun, dass .meltDateien keine Kommentare unterstützen, was ich mir wünschte (Sie erhalten Fehlermeldungen für jede Zeile, die ein nicht analysierbares Argument enthält, einschließlich solcher mit beispielsweise a #). Also hier ist eine kommentierte test_c.meltDatei:

# master comment here

colour:blue  # this is blue!
out=24

colour:red
out=48

Wenn Sie dies meltdirekt öffnen, erhalten Sie:

$ melt test_c.melt
Failed to load "# master comment here"
...

... und es wird kein blauer Bildschirm angezeigt.

Also dachte ich mir - nun, ich kann trotzdem Kommentare einfügen und dann die Bash-Prozessersetzung verwenden, um die Datei zu filtern sed, und diese einfach der meltAnwendung zur Verfügung stellen. Zuerst habe ich einen Test mit versucht cat, der erfolgreich ist:

$ cat <(sed 's/#.*$//' test_c.melt)



colour:blue  
out=24

colour:red
out=48

... sieht gut aus; aber wenn ich das mit versuche, meltsieht es durch meinen Trick:

$ melt <(sed 's/#.*$//' test_c.melt)
Failed to load "/dev/fd/62"
Failed to load "/dev/fd/62"

Grundsätzlich hat Melt den Dateinamen des Rohrs Bash erhalten, das für die Prozessersetzung bereitgestellt wurde - aber leider meltwird es argv[i]direkt verarbeitet . und im Falle einer Datei muss eine .meltErweiterung im Dateinamen angezeigt werden; Wenn dies nicht der Fall ist, schlägt der Prozess fehl.

Meine Frage lautet also: Wie könnte ich die Prozessersetzung verwenden - also hat der Dateiname der Pipe in diesem Fall eine bestimmte Erweiterung .melt? Grundsätzlich möchte ich als Ergebnis der Substitution einen Pipe-Dateinamen von /dev/fd/62.melt, von dem ich denke, dass er übergeben wird.

NB: Natürlich kann ich immer tun:

sed 's/#.*$//' test_c.melt > test_c_temp.melt
melt test_c_temp.melt

... aber zuerst gibt es hier zwei Befehle - und ich möchte eine einzeilige Pipeline; und zum anderen eröffnet es ein weiteres Problem, wenn ich darüber nachdenke, temporäre Dateien danach zu entfernen, was mir nicht gefällt.

Ist dies mit Bash-Prozessersetzung möglich - oder irgendwie mit Standard-Linux-Tools?

sdaau
quelle
1
Versuchen Siemelt < <(sed 's/#.*$//' test_c.melt)
Costas
Danke @Costas - habe das versucht, aber anscheinend meltkümmert es mich nicht um Befehle, die durchgeleitet werden stdin, da ich: Usage: melt [options] [producer [name=value]* ]+und einen Speicherauszug von Programmoptionen bekomme . Prost!
Sdaau
1
Ich muss man meltfür dich lesen . " Wenn keine Dateien angegeben sind, wird die Komprimierung auf die Standardeingabe angewendet ." So sed 's/#.*$//' test_c.melt | melt > result.filearbeiten können.
Costas
Nochmals vielen Dank, @Costas - leider funktioniert das auch nicht, zumindest nicht bei meiner Version von melt(MLT Melt 0.6.2; "Standardeingabe" wird in dieser Version nicht erwähnt man melt), da ich für diesen Befehl wieder Usage: melt [options] [producer [name=value]* ]+und bekomme eine Müllkippe. Prost!
Sdaau
Ich denke, Sie müssen man meltauf Ihrer Maschine lesen . Wenn Sie keine Antwort finden können, posten Sie auf pastebin.com und posten Sie den Link hier.
Costas

Antworten:

3

Eine Möglichkeit wäre, meltauf ein Dateisystem zu verweisen , das geänderte Kopien von Dateien anzeigt. FUSE ist eine generische Methode zum Erstellen eines Dateisystemtreibers, der von einem normalen Programm implementiert wird und keine Berechtigungen erfordert. Es gibt viele FUSE-Dateisysteme , und es besteht eine gute Chance, dass eines davon Ihnen helfen kann. Die Idee ist, einen Einhängepunkt bereitzustellen, an dem das Lesen einer .meltDatei die „echte“ Datei liest, wobei jedoch Kommentare herausgefiltert werden.

ScriptFS sieht vielversprechend aus (aber ich habe es nie benutzt). So etwas sollte funktionieren:

mkdir ~/uncommented-melt
scriptfs -p "$HOME/bin/uncomment-melt;&*.melt" ~/work ~/uncommented-melt

Wo ~/workist die Wurzel des Baums, der Ihre .meltDateien enthält und ~/bin/uncomment-meltist

#!/bin/sh
sed 's/#.*$//' "$1"

Wenn Sie dann eine Datei ~/work/test_c.meltmit Kommentaren haben, können Sie diese ausführen melt ~/uncommented-melt/test_c.melt.

Andere potenziell hilfreiche FUSE-Dateisysteme:

  • Execfuse - Mit dieser Option können Sie einen einfachen FUSE-Treiber mit Shell-Skripten erstellen
  • AVFS oder andere FUSE-Dateisysteme, die Dateien transparent dekomprimieren : Definieren Sie das Entfernen von Kommentaren als Dekomprimierungsregel.
Gilles 'SO - hör auf böse zu sein'
quelle
Vielen Dank für das @ Gilles - ich habe diese Antwort akzeptiert, weil sie die Frage generisch beantwortet. Leider konnte ich nicht ganz bekommen scriptfszu arbeiten - hier ist ein Befehlsprotokoll ich zu der Zeit gespeichert (AFAICR, Stack an diesem Tag abgestürzt :)) gist.github.com/anonymous/e9ee5e8c53d1b4c3c736 - so endete ich mit einem up - bashSkript Ansatz, den ich geschrieben unten. Prost!
Sdaau
2

Mit der zshShell können Sie die =(...)Form der Prozessersetzung verwenden (bei der temporäre Dateien anstelle von /dev/fd/xDateien und Pipes verwendet werden), für die Sie ein Suffix angeben können:

(TMPSUFFIX=.melt; melt =(sed 's/#.*$//' test_c.melt))

Weitere Informationen finden Sie unter info zsh TMPSUFFIX(vorausgesetzt, die infoSeiten sind installiert, Sie müssen möglicherweise ein zsh-docPaket installieren ).

Stéphane Chazelas
quelle
1

Ok, es stellt sich heraus, dass in meinem speziellen Fall diese Art von Schmelzskripten streng als Befehlszeilenargumente zu interpretieren sind. Nur der sedim OP schneidet es also nicht ganz ab (außerdem gibt es andere Dinge wie Profile, die eingestellt werden können). Hier ist also, was ich letztendlich getan habe - kann wahrscheinlich als Inspiration in anderen Fällen dienen, die der Titel des OP abdecken würde.

Ich habe mich schließlich dazu entschlossen: eine test.shmeltDatei zu erstellen, die eigentlich ein bashSkript ist, das meinen kommentierten meltSkriptcode enthält; diese Datei ausführbar machen chmod +x test.shmelt; Führen Sie das Skript nach dem Bearbeiten als aus ./test.shmelt.

Beim Ausführen wird eine "bereinigte" test.meltDatei erstellt /tmpund meltstattdessen diese Datei aufgerufen . Da meltdiese temporäre Datei normalerweise nach dem Ende des Programms im Terminal weiter ausgeführt wird, kann diese temporäre Datei mit einem Trap auf SIGINT bereinigt werden, wenn Strg-C gedrückt wird (muss aber nicht).

Auf diese Weise habe ich noch Kommentare; kann schnell in der Quelldatei bearbeiten und Melt ausführen und Ergebnisse anzeigen; und habe eine Datei "bereinigt" von Kommentaren, die ich später verwenden kann.

Hier ist der Code von test.shmelt:

#!/bin/bash

# call with: ./test.shmelt


#TMLTFILE="test.melt" # for final export
TMLTFILE="${0%%.shmelt}.melt" # for final export

function finished() { echo ; } ; # use this when testing or montaging to keep exported (tmp) .melt file; uncomment the below to remove the tmp file
#~ function finished() { rm /tmp/"$TMLTFILE"; echo "fin1 $0" ; } ; trap finished SIGINT ;

echo 'Remember `pulseaudio --start` to hear audio with `melt`!'
pulseaudio --check
if [ $? -ne 0 ]; then
    pulseaudio --start
fi

DIRNAME=$(readlink -f $(dirname $0))
PROFILE="square_ntsc"


echo "
# the profile doesn't work from here;
# still has to be specified on command line
# but including it here so the path is saved for testing
#~ -profile
#~ ${PROFILE}

-video-track

  # can avoid pixmap: here, but that s the producer;
  # qimage: also works
  # NB it is NOT '-out 1645'; but 'out=1645'!!
  /media/myimg/%05d.bmp
  in=0
  out=1645
  #~ length=1645
  #~ loop=0
  #~ eof=stop

-audio-track

  /media/mysnd/snd.wav
  in=0
  out=1645
  #~ loop=0
  #~ eof=stop

#-consumer xml # doesn't work here

" | sed -e 's/#.*$//' -e 's/^[[:space:]]*//' -e '/^[[:space:]]*$/d' -e 's/[[:blank:]]*$//' > ${TMLTFILE}

# the sed: remove comments; remove indents; remove empty lines; remove spaces at end of line


# eof: one of: stop, loop, continue or pause (eof=stop)
# to loop, use `melt eof=loop ...` (but make sure first,
#  that the edit as a whole stops - use xml to check!)
# due to caching issues, preview pieces (up to ~300 frames) like this:
melt eof=loop -profile ${PROFILE} ${TMLTFILE} in=200 out=400

# must have profile here, for checking w/ -consumer xml (else segfault)
# this command may add additional producers to the xml!:
## melt -profile ${PROFILE} ${TMLTFILE} -consumer xml

# use this to generate xml if needed:
#melt -profile ${PROFILE} $(cat ${TMLTFILE} | tr '\n' ' ') -consumer xml

# if exited normally, cleanup:
finished
sdaau
quelle
1

Es ist eine Weile her, aber ich werde versuchen, die Titelfrage auf eine Weise zu beantworten, die für mich nützlich gewesen wäre.

Dies hängt zusammen: Warum funktioniert die BASH-Prozessersetzung bei einigen Befehlen nicht?

Das spezifische Problem, das ich zu lösen versuchte, war folgendes:

  1. Ich habe einen Bildkonverter.
  2. es konvertiert von [zufälliges Format einfügen] in einfache Formate wie BMP / PNM / TGA;
  3. Ich möchte in JPEG konvertieren, muss es also mit ImageMagick verketten convert.
  4. Ich möchte keine temporären Dateien verwenden.

Leider gibt das Tool nicht gerne in stdout / fds / pipes aus, da es das Ausgabeformat von der Erweiterung des Ausgabedateinamens ableitet. Also, wie man es austrickst?

Erstellen Sie einen Softlink mit der entsprechenden Erweiterung /dev/fd/Nund verwenden Sie diesen als "temporäre Datei".

Beispiel:

ln -s /dev/fd/4 temp.pnm
[TOOL] -i [INPUTFILE] -o temp.pnm 4>&1 | convert - [OUTPUT.JPG]
Nuno Cruces
quelle