开发者

Bash: Insert line into source code file after initial comments

开发者 https://www.devze.com 2023-03-17 18:38 出处:网络
I need to use bash to insert a line into a python file. This line needs to appear after any initial comments in the the file.

I need to use bash to insert a line into a python file. This line needs to appear after any initial comments in the the file.

So given the file:

#!/usr/bin/python
# This is just 
# an example comment

moo = "cow"
... etc ...

I need a bash command to insert a new line like this:

#!/usr/bin/python
# This is just 
# an example comment
NEW LINE GOES HERE

moo = "cow"
... etc ...

I am entirely stumped on how to do this. I have tried looping over the file line by line, but that just ends up being pretty horrific and severely messing up the file's whitespace.

Any suggestions would be great!

Adam

PS. Yes, this开发者_开发百科 is a bit of a weird thing to do, it is for part of a continuous integration build script.


Edit

For the record, the code I was trying was:

insert_setup_code() {
    installed=false
    tmpfile="/tmp/$RANDOM"

    cat "$INSTALL_TO" | while read -d \n l; do
        echo "$l" >> $tmpfile
        if [[ ! $installed && ! `echo "$l" | grep "^#"` ]]; then
            echo "LINE OF CODE HERE" >> $tmpfile
            installed=true
        fi
    done
}


I would write:

line="NEW STUFF HERE"
awk -v text="$line" '!/^#/ && !p {print text; p=1} 1' file

The first non-comment line will trigger the block to print the line:

  • !/^#/ -- line does not start with a hash
  • !p -- variable p is not true


there you go

my addline script. add newline after any initial comments in the filein and write to fileout

#!/usr/bin/env bash

newline="$1"
filein="$2"
fileout="$3"
added=0

while read -r; do
    if ! ((added)) && ! [[ $REPLY =~ ^# ]]; then
        printf "%s\n" "$newline" >> "$fileout"
        ((added++))
    fi
    printf "%s\n" "$REPLY" >> "$fileout"
done < "$filein"

Use as:

$ bash addline "my new line" "readThisFile" "writeToThisFile"

adjust to your needs :)


example usage to itself:

$ bash addline "# a test comment line" addline foo
$ cat foo

#!/usr/bin/env bash
# a test comment line

newline="$1"
filein="$2"
fileout="$3"
added=0

while read -r; do
    if ! ((added)) && ! [[ $REPLY =~ ^# ]]; then
        printf "%s\n" "$newline" >> "$fileout"
        ((added++))
    fi
    printf "%s\n" "$REPLY" >> "$fileout"
done < "$filein"

Updated faster version:
using wc -l and sed to write the rest of the file instead of looping through each line

#!/usr/bin/env bash

newline="$1"
filein="$2"
fileout="$3"
counter=0

while read -r; do
    ((counter++))
    if ! [[ $REPLY =~ ^# ]]; then
        printf "%s\n" "$newline" "$REPLY" >> "$fileout"
        break
    fi
    printf "%s\n" "$REPLY" >> "$fileout"
done < "$filein"

sed -n "$counter,$(wc -l < "$filein")p" "$filein" >> "$fileout"

works as before/above


Using ed there is no need for a tmp file.

The following code assumes that there are only empty lines or lines beginning with a # char before the first non-empty line that does not begin with a # char.

# insert a line just before the first line that does not begin with a '#' char
# skips empty lines and lines containing whitespace characters only
# for more information on ed see http://wiki.bash-hackers.org/howto/edit-ed

cat <<-'EOF' | ed -s file
H
/^[[:space:]]*[^#[:space:]]/i
NEW STUFF HERE
.
wq
EOF

If you insist that the line gets inserted exactly after the initial comments, you can do that as well.

# using FreeBSD ed (on Mac OS X)
cat <<-'EOF' | ed -s file
H
,v/^#/u\
u\
i\
NEW STUFF HERE\
.
wq
EOF


# using sed & ed
# first get the line number of the first line not beginning with a '#' char;
# then use ed for in-place file editing
lno=$(sed -n '/^#/!{=;q;}' file) &&
cat <<-EOF | ed -s file
H
${lno},${lno}i
NEW STUFF HERE
.
wq
EOF


you could add the stub, your new line, and remaining stuff as variables in the bash script, i.e.

HEADER="!#/usr/bin/bla bla.."

YOUR_LINE="NEW LINE GOES HERE"

REST="more stuff"

and pipe them into your new script

echo "$HEADER" "$YOUR_LINE" "$REST" > pytonscript.py

i gues the YOUR_LINE variable is dynamic, but if the HEADER and REST are static that should work, if these are dynamic as well, then you could use head and tail in combination with wc -l to calculate what lines should be included


FYI, your bash version would go like this

insert_setup_code() {
        install_to="$1"
        tmp=$(mktemp -t setup)

        added=0
        while IFS=$'\n' read -r line ; do
                printf "$line\n" >> "$tmp"
                [ $added = 0 ] && grep -q '^#' <<<"$line" && printf "LINE OF CODE HERE\n" >>"$tmp" && added=1
        done < "$install_to"

        mv -f "$tmp" "$install_to"
}

But of course additional setps should be taken if you want to preserve metadata on the $install_to file


Locate the first empty line

$ grep -n -m1 '^$' input | cut -d: -f1 
4

Use that line number and change it to some other string, -i to edit files in place (use with care!)

$ sed -i '4c NEW STUFF HERE' input

$ cat input
#!/usr/bin/python
# This is just 
# an example comment
NEW STUFF HERE
moo = "cow"
... etc ...

A different approach is to grep for the first line that do not contain a #, then the above becomes:

$ grep -n -m1 '^[^#]' input | cut -d: -f1
5

$ sed -i "5i NEW STUFF HERE" input

$ cat input
#!/usr/bin/python
# This is just 
# an example comment

NEW STUFF HERE
moo = "cow"
... etc ...

To use a shell variable in a sed-script, use this aproach, where " allow variables to be expanded

sed "${num}c NEW STUFF HERE" log
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号