Wie kann ich alle Kommentare aus einer Datei entfernen?

21

Ich habe eine Datei mit Kommentaren:

foo
bar
stuff
#Do not show this...
morestuff
evenmorestuff#Or this

Ich möchte nur den gesamten unkommentierten Code ausdrucken:

foo
bar
stuff
morestuff
evenmorestuff

Es ist so wichtig, dass Kommentare aus einer Datei entfernt werden können ... Was ist ein guter Weg, dies zu tun?

Fragezeichen
quelle
1
Sie können mit grep keine Teile einer Zeile entfernen. Sie können sed für diese verwenden
miracle173
2
Dein Text und dein Beispiel widersprechen sich. Sie schreiben über Zeilen, die auskommentiert werden, aber deutlich ab der letzten Zeile meinen Sie Zeilenteile. Und dann wird die erste Zeile mit einem Kommentar einschließlich EOL gelöscht, und die zweite Sekunde könnte sein, aber es ist nicht klar, da dies die letzte Zeile ist. Bitte formulieren Sie 'Zeilen auskommentiert' neu, um Ihre Beispiele zu verdeutlichen.
Anthon
5
versuchen Sie es mit awk -F\# '$1!="" { print $1 ;} '.
Archemar
2
Wie würde eine Leitung echo '#' # output a #behandelt werden?
Kusalananda
3
@Questionmark Ich bin vielleicht schlau, aber ich schreibe keinen schlauen Shell-Grammatik-Parser.
Kusalananda

Antworten:

39

Eine Möglichkeit , alle Kommentare zu entfernen , ist die Verwendung grepmit -oOption:

grep -o '^[^#]*' file

woher

  • -o: druckt nur einen übereinstimmenden Teil der Zeile
  • Erstens ^: Beginn der Zeile
  • [^#]*: beliebiges Zeichen außer #null oder mehrmals wiederholt

Beachten Sie, dass auch leere Zeilen entfernt werden, aber Zeilen mit nur Leerzeichen bleiben.

jimmij
quelle
2
Ich würdegrep -v '^#' file > newfilewithoutcomments
Basile Starynkevitch
1
Es sollte beachtet werden, dass dies KEINE allgemeine Methode für Shell-Skripte ist, da zum Beispiel die Zeile somvar='I am a long complicated string ## with special characters' # and I am a commentnicht korrekt behandelt wird.
Wildcard
Diese Variante funktioniert besser für mich (auf einem Mac):grep -o '^[^#].*' file
Pierz
Die Kommentare sind weg, aber ich sehe eine Menge Leerzeichen an ihrer Stelle in der Ausgabe? sedLösung hat nur eine leere Zeile, scheint ein solides Argument zu sein, um eine andere Antwort zu verwenden, es sei denn, ich vermisse etwas?
JBallin
@JBallin Hast du einen Alias ​​für grepvielleicht definiert? Versuchen Sie , grepzu command grep, wenn man noch Räume , die Abtastwerteingang posten.
Jimmy
31

Ich glaube, das sedkann man viel besser als grep. Etwas wie das:

sed '/^[[:blank:]]*#/d;s/#.*//' your_file

Erläuterung

  • sedIn der Standardeinstellung wird Ihre Datei zeilenweise überprüft und jede Zeile gedruckt, nachdem möglicherweise die Transformationen in den Anführungszeichen angewendet wurden. ( sed '' your_filedruckt nur alle Zeilen unverändert aus).
  • Hier geben wir sedzwei Befehle für jede Zeile ein (sie sind durch ein Semikolon getrennt).
  • Der erste Befehl sagt: /^[[:blank:]]*#/d. Auf Englisch bedeutet dies, dass, wenn die Zeile am Anfang mit einem Hash übereinstimmt (mit vorangestellter Anzahl von Leerzeichen), diese Zeile gelöscht wird (sie wird nicht gedruckt).
  • Der zweite Befehl ist: s/#.*//. Ersetzen Sie im Englischen also eine Raute gefolgt von so vielen Dingen wie möglich (bis zum Ende der Zeile) durch nichts (nichts ist der leere Raum zwischen den letzten beiden //).
  • Zusammenfassend durchläuft dies Ihre Datei und löscht Zeilen, die vollständig aus Kommentaren bestehen. In den verbleibenden Zeilen werden die Kommentare gestrichen.
Joseph R.
quelle
1
Es wird auch alles gelöscht, was nach einem Hash in einem String gefunden wurde , nicht wahr? ZB mystring="Hello I am a #hash" wird mystring="Hello I am a"
Javadba
@javadba, ja, aber zu diesem Zeitpunkt können Sie auch einen vollständigen Parser verwenden. Was wird mit diesen Daten geschehen, die Anführungszeichen und Variablenzuweisungen verstehen, aber keine Kommentare verarbeiten können? (Aus diesem Grund crontaberlauben viele Konfigurationsdateien, wie zum Beispiel, nur vollständige Kommentare mit oder ohne vorangestelltem Leerzeichen, aber keine nachgestellten Kommentare in einer Zeile. Die Logik ist VIEL einfacher. Verwenden Sie in dieser Antwort nur die erste der beiden Sed-Anweisungen für eine Crontab Kommentar Stripper.)
Wildcard
Gute Antwort, dies scheint ein gutes Gleichgewicht zwischen Nutzen und Komplexität für eine Vielzahl von allgemeinen Anwendungsfällen zu sein, aber in dem Fall, dass Sie im Voraus wissen, dass Sie nur Zeilen löschen müssen, die direkt mit #(in Spalte 1) beginnen, Gibt es irgendeinen Vorteil für sedover grep -v "^#"?
RBF06,
4

Sie können die gewünschte Ausgabe mit dem Befehl sed erzielen. Der folgende Befehl hatte den Trick für mich getan.

sed 's/#.*$//g' FileName

Woher

  • #.*$- Regexp filtert alle Zeichenfolgen, die #bis zum Ende der Zeile beginnen

Hier müssen wir diese Zeilen entfernen, also ersetzen wir sie durch ein leeres, also überspringendes Ersatzteil.

  • g - Erwähnung der wiederholten Suche im Muster bis zum Ende der Datei.

Allgemeine Syntax von sed: s/regexp/replacement/flags FileName

Sridhar DD
quelle
2
Hinweis: In diesem Fall wird die vierte Zeile durch eine neue Zeile ersetzt.
αғsнιη
1
Versuchen Sie das mit einem Skript, das diesen sedBefehl enthält ...
Kusalananda
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
3

Wie bereits erwähnt, funktionieren sed und andere textbasierte Tools nicht gut, wenn Teile eines Skripts wie Kommentare aussehen, dies jedoch nicht der Fall ist. Zum Beispiel könnten Sie ein # in einer Zeichenkette finden, oder das eher gebräuchliche $#und ${#param}.

Ich habe einen Shell-Formatierer namens shfmt geschrieben , der eine Funktion zum Minimieren von Code enthält. Dazu gehört unter anderem das Entfernen von Kommentaren:

$ cat foo.sh
echo $# # inline comment
# lone comment
echo '# this is not a comment'
[mvdan@carbon:12] [0] [/home/mvdan]
$ shfmt -mn foo.sh
echo $#
echo '# this is not a comment'

Der Parser und der Drucker sind Go-Pakete. Wenn Sie also eine benutzerdefinierte Lösung wünschen, sollte es ziemlich einfach sein, ein 20-zeiliges Go-Programm zu schreiben, um Kommentare genau so zu entfernen, wie Sie es möchten.

Daniel
quelle
2

Sie können invert match wie folgt verwenden:

    #grep -v "#" filename

-v, --invert-match Invertiert den Übereinstimmungssinn, um nicht übereinstimmende Linien auszuwählen. (-v wird von POSIX angegeben.)

Raza
quelle
2
@alinh Vielen Dank für die Überprüfung der Antwort. Bitte beachten Sie, dass für die Frage nicht nur der Zeilenanfang, sondern eine beliebige Stelle in der Datei erforderlich ist. Dies zeigt sich auch in dem erwarteten Ergebnis in der obigen Frage. Meine Antwort wäre falsch, wenn ich nur den Zeilenanfang suche.
Raza
zzz. Mein Schlechtes, habe die letzte Zeile nicht gesehen :(
alinh
1
Dadurch wird die Zeile beginnend mit evenmorestuffim Beispiel des OP vollständig entfernt .
Joseph R.
@ JosephR. guter Fang. Das habe ich früher verpasst. In diesem Fall grep -o '^[^#]*' filewäre die beste Lösung. das erklärt schon jimmij. Vielen Dank für Ihre Bewertung
Raza
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
2

Ich mag Josephs Antwort, brauchte sie aber, um // Kommentare zu entfernen, also habe ich sie leicht modifiziert und auf redhat getestet

# no comments alias
alias nocom="sed -E '/^[[:blank:]]*(\/\/|#)/d;s/#.*//' | strings"

# example
cat SomeFile | nocom | less

Ich wette, es gibt eine bessere Möglichkeit, Leerzeilen zu entfernen, als Zeichenfolgen zu verwenden, aber es war die schnelle und schmutzige Lösung, die ich verwendet habe.

-Prost

Brandon
quelle
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
2

Das hat bei mir funktioniert

sed -i.old -E  "/^(#.*)$/d" file 
David Okwii
quelle
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
1
cat YOUR_FILE | cut -d'#' -f1

Es wird #als Spaltentrennzeichen verwendet und behält nur die erste Spalte (das ist alles vorher #).

Alexey
quelle
1
Wenn YOUR_FILEes sich um ein Skript handelt, das diese Befehle enthält, verbleibt das Skript cat YOUR_FILE | cut -'in der Datei in dieser Zeile.
Kusalananda
1

Verwenden Sie Ausdruck wie

egrep -v "#|$^" <file-name> 

: -v: invertiert die Übereinstimmung

: #: stimmt mit allen Zeilen überein, die mit # beginnen

: $ ^: stimmt mit allen Leerzeilen überein

aditya
quelle
1
Nein, das #stimmt überall auf der Leitung überein und entfernt die gesamte Leitung.
Ilkkachu
1

Die beste Lösung wäre, den Befehl zu verwenden:

sed -i.$(date +%F) '/^#/d;/^$/d' ntp.conf

Das -i ist die direkte Bearbeitung, aber das direkt folgende Präfix weist sed an, ein Backup zu erstellen. In diesem Fall mit einer Datumserweiterung (ntp.conf.date) führen wir zwei Befehle mit jeweils einem Adressraum aus. Der erste löscht die kommentierten Zeilen und der zweite, der durch ein Semikolon vom ersten getrennt ist, löscht die leeren Zeilen.

Ich habe diese Lösung auf theurbanpenguin.com gefunden

jyoti
quelle
0

Keine der anderen Antworten scheint dies zu rechtfertigen. Entweder stehen sie in leeren Zeilen oder in Zeilen, in denen sich der Kommentar nicht im ersten Zeichen befindet. Am Ende habe ich Folgendes verwendet:

cat << EOF >> ~/.bashrc
alias nocom='sed -e "/^\s*#/d" -e "/^\s*$/d"'
EOF

Dadurch wird ein Alias ​​erstellt, sodass Sie sich diesen nicht merken müssen (was zunächst unmöglich ist). Öffnen Sie eine neue Sitzung und Sie erhalten den neuen nocomBefehl. Dann kannst du einfach

nocom /etc/foobar.conf

Prost.

bviktor
quelle
1
Es hat nicht viel Sinn, .*$in der ersten Regex eine Übereinstimmung zu finden - der Anker ist nicht nützlich und Sie erfassen nicht den übereinstimmenden Text, der in einem Ersatz verwendet werden soll. benutze einfach^\s*
Jeff Schaller
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
0

Nach der zweiten Antwort von Joseph R. /^$/dentferne ich eine Leerzeile.

sed '/^[[:blank:]]*#/d;s/#.*//;/^$/d'
Pierre-Damien
quelle
-1

Ich poste, was für mich funktioniert und am sinnvollsten erscheint, nachdem ich die anderen mit Erklärungen durchgelesen habe. Ein paar Beiträge kamen nahe, aber ich konnte noch keinen Kommentar abgeben (weil ich ein Neuling bin):

grep -E -v "(^#.*|^$)" filename
  • -E = interpretiere das folgende Muster als regulären Ausdruck, ähnlich wie egrep
  • -v = Invertierung des Musters drucken (Zeilen, die nicht zum Ausdruck passen, werden gedruckt)
  • "(^#.*|^$)"= dies hat eine Pipe, die eine OR-Anweisung bezeichnet. Dieser Ausdruck sagt, dass jede Zeile, die mit einem #(und irgendetwas anderem danach) beginnt, ODER jede Zeile mit null Zeichen zwischen dem Anfang und dem Ende der Zeile gedruckt werden soll .

Das -vwird auf dem Bildschirm die Umkehrung von dem ausgeben, die jede Zeile mit Zeichen sein wird, die nicht mit einem beginnt #.

jackbmg
quelle
Es geht nichtprint "#tag" # Print a hashtag.
Ray Butterworth
Ah, richtig ... natürlich. Vielen Dank für den Hinweis. Ich suchte nach einer Antwort in Bezug auf typische Linux-Konfigurationsdateien wie pam.d configs, also habe ich nicht daran gedacht. Ich denke, es müsste angepasst werden, um Kommentare zu finden und zu entfernen, die in derselben Zeile wie der Code stehen. Ich habe gerade eine wahrscheinlich bessere Lösung für mein spezielles Problem oben gesehen: egrep -v "# | $ ^"
jackbmg