ChatGPT解决这个技术问题 Extra ChatGPT

How to insert a text at the beginning of a file?

So far I've been able to find out how to add a line at the beginning of a file but that's not exactly what I want. I'll show it with an example:

File content

some text at the beginning

Result

<added text> some text at the beginning

It's similar but I don't want to create any new line with it...

I would like to do this with sed if possible.

Anything in here work for you? stackoverflow.com/questions/54365/… - you don't need to use sed.

C
Cy Rossignol

sed can operate on an address:

$ sed -i '1s/^/<added text> /' file

What is this magical 1s you see on every answer here? Line addressing!.

Want to add <added text> on the first 10 lines?

$ sed -i '1,10s/^/<added text> /' file

Or you can use Command Grouping:

$ { echo -n '<added text> '; cat file; } >file.new
$ mv file{.new,}

To actually insert a new line: sed -i '1s/^/ \n/' file
Why using -i ?? On man it says that it is for suffix. linux.die.net/man/1/sed
-i stands for in-place, you can append a suffix to -i to make a copy rather than overwrite. -i.new would make a new file ending with .new, but just -i would edit the file directly.
Note that the sed won't work on an empty file - afaict sed can't be made to do anything at all with 0-length input.
The flaw of this solution is that it doesn't add the text if file is empty.
D
Denim Datta

If you want to add a line at the beginning of a file, you need to add \n at the end of the string in the best solution above.

The best solution will add the string, but with the string, it will not add a line at the end of a file.

sed -i '1s/^/your text\n/' file

On Mac OS, was getting error with "undefined label". Found that you need to give an extension for a backup file; see mkyong.com/mac/…
Under Mac OS's sed version you have to supply backup file name with -i option. One can pass just an empty string for no backup like sed -i '' '1s/^/new test\n/' filename
p
paxdiablo

If the file is only one line, you can use:

sed 's/^/insert this /' oldfile > newfile

If it's more than one line. one of:

sed '1s/^/insert this /' oldfile > newfile
sed '1,1s/^/insert this /' oldfile > newfile

I've included the latter so that you know how to do ranges of lines. Both of these "replace" the start line marker on their affected lines with the text you want to insert. You can also (assuming your sed is modern enough) use:

sed -i 'whatever command you choose' filename

to do in-place editing.


N
Nick Roz

Use subshell:

echo "$(echo -n 'hello'; cat filename)" > filename

Unfortunately, command substitution will remove newlines at the end of file. So as to keep them one can use:

echo -n "hello" | cat - filename > /tmp/filename.tmp
mv /tmp/filename.tmp filename

Neither grouping nor command substitution is needed.


this solution delete empty lines at the end of the file
This is the superior and simpler solution to all the other ones using sed, which don't work for empty files.
@dafnahaktana that's because of command substitution
佚名

To insert just a newline:

sed '1i\\'

I wonder why the top voted answers all use s instead of i. Any downsides to this?
@Caesar Because when all you have is a hammer, every problem looks like a nail.
g
gniourf_gniourf

You can use cat -

printf '%s' "some text at the beginning" | cat - filename

This would only output the text followed by the file's content, but it does not modify the file at all.
That's a good solution, I wonder why it didn't get any upvotes. Here's mine my good sir. Also, why printf and not a simple echo ?
@ychaouche - presumably because there's no portable way to prevent echo from adding a newline?
I tried directing this into a file by appending > file to the command, it jus spammed my terminal with "some text at the beginning"
printf '%s' "some text" | cat - filename > tmpfile && mv tmpfile filename
s
solidsnack

To add a line to the top of the file:

sed -i '1iText to add\'

This includes a newline, which the questioner explicitly doesn't want.
v
vardhan

my two cents:

sed  -i '1i /path/of/file.sh' filename

This will work even is the string containing forward slash "/"


x
xck

Hi with carriage return:

sed -i '1s/^/your text\n/' file

C
Curt Clifton

Note that on OS X, sed -i <pattern> file, fails. However, if you provide a backup extension, sed -i old <pattern> file, then file is modified in place while file.old is created. You can then delete file.old in your script.


Or sed -i '' file. Then it'll just edit in-place and not make backups.
I've found macOS sed wants a dot to edit in place without creating a backup: sed -i. file
R
Raphael Villas Boas

There is a very easy way:

echo "your header" > headerFile.txt
cat yourFile >> headerFile.txt

Ow... sorry, now that I realize that you wanted to add at the beginning and not a new line.
But I wanted a new line. So thanks for the simplistic approach.
V
Victoria Stuart

PROBLEM: tag a file, at the top of the file, with the base name of the parent directory.

I.e., for

/mnt/Vancouver/Programming/file1

tag the top of file1 with Programming.

SOLUTION 1 -- non-empty files:

bn=${PWD##*/}    ## bn: basename

sed -i '1s/^/'"$bn"'\n/' <file>

1s places the text at line 1 of the file.

SOLUTION 2 -- empty or non-empty files:

The sed command, above, fails on empty files. Here is a solution, based on https://superuser.com/questions/246837/how-do-i-add-text-to-the-beginning-of-a-file-in-bash/246841#246841

printf "${PWD##*/}\n" | cat - <file> > temp && mv -f temp <file>

Note that the - in the cat command is required (reads standard input: see man cat for more information). Here, I believe, it's needed to take the output of the printf statement (to STDIN), and cat that and the file to temp ... See also the explanation at the bottom of http://www.linfo.org/cat.html.

I also added -f to the mv command, to avoid being asked for confirmations when overwriting files.

To recurse over a directory:

for file in *; do printf "${PWD##*/}\n" | cat - $file > temp && mv -f temp $file; done

Note also that this will break over paths with spaces; there are solutions, elsewhere (e.g. file globbing, or find . -type f ... -type solutions) for those.

ADDENDUM: Re: my last comment, this script will allow you to recurse over directories with spaces in the paths:

#!/bin/bash

## https://stackoverflow.com/questions/4638874/how-to-loop-through-a-directory-recursively-to-delete-files-with-certain-extensi

## To allow spaces in filenames,
##   at the top of the script include: IFS=$'\n'; set -f
##   at the end of the script include: unset IFS; set +f

IFS=$'\n'; set -f

# ----------------------------------------------------------------------------
# SET PATHS:

IN="/mnt/Vancouver/Programming/data/claws-test/corpus test/"

# https://superuser.com/questions/716001/how-can-i-get-files-with-numeric-names-using-ls-command

# FILES=$(find $IN -type f -regex ".*/[0-9]*")        ## recursive; numeric filenames only
FILES=$(find $IN -type f -regex ".*/[0-9 ]*")         ## recursive; numeric filenames only (may include spaces)

# echo '$FILES:'                                      ## single-quoted, (literally) prints: $FILES:
# echo "$FILES"                                       ## double-quoted, prints path/, filename (one per line)

# ----------------------------------------------------------------------------
# MAIN LOOP:

for f in $FILES
do

  # Tag top of file with basename of current dir:
  printf "[top] Tag: ${PWD##*/}\n\n" | cat - $f > temp && mv -f temp $f

  # Tag bottom of file with basename of current dir:
  printf "\n[bottom] Tag: ${PWD##*/}\n" >> $f
done

unset IFS; set +f

m
merlin2011

Just for fun, here is a solution using ed which does not have the problem of not working on an empty file. You can put it into a shell script just like any other answer to this question.

ed Test <<EOF
a

.
0i
<added text>
.
1,+1 j
$ g/^$/d
wq
EOF

The above script adds the text to insert to the first line, and then joins the first and second line. To avoid ed exiting on error with an invalid join, it first creates a blank line at the end of the file and remove it later if it still exists.

Limitations: This script does not work if <added text> is exactly equal to a single period.


q
qwerty_so
echo -n "text to insert " ;tac filename.txt| tac > newfilename.txt

The first tac pipes the file backwards (last line first) so the "text to insert" appears last. The 2nd tac wraps it once again so the inserted line is at the beginning and the original file is in its original order.


Can you please explain how this would help Tomas with the issue?
tac | tac -- that's how non-scalable mindsets form :-(
0
0x8BADF00D

Another solution with aliases. Add to your init rc/ env file:

addtail () { find . -type f ! -path "./.git/*" -exec sh -c "echo $@ >> {}" \; }
addhead () { find . -type f ! -path "./.git/*" -exec sh -c  "sed -i '1s/^/$@\n/' {}" \; }

Usage:

addtail "string to add at the beginning of file"
addtail "string to add at the end of file"

k
kakyo

With the echo approach, if you are on macOS/BSD like me, lose the -n switch that other people suggest. And I like to define a variable for the text.

So it would be like this:

Header="my complex header that may have difficult chars \"like these quotes\" and line breaks \n\n "

{ echo "$Header"; cat "old.txt"; } > "new.txt"
mv new.txt old.txt

M
Murilo Perrone

The simplest solution I found is:

echo -n "<text to add>" | cat - myFile.txt | tee myFile.txt

Notes:

Remove | tee myFile.txt if you don't want to change the file contents.

Remove the -n parameter if you want to append a full line.

Add &> /dev/null to the end if you don't want to see the output (the generated file).

This can be used to append a shebang to the file. Example: # make it executable (use u+x to allow only current user) chmod +x cropImage.ts # append the shebang echo '#''!'/usr/bin/env ts-node | cat - cropImage.ts | tee cropImage.ts &> /dev/null # execute it ./cropImage.ts myImage.png


This appends the text twice in the same line
It also replaces the content of the entire file.
@DavidHeisnam, yes it replaces the file, unless you remove the tee pipe (I have updated the answer). But It won't append the text twice. You may have run the command twice. It's working in bash and zsh.
K
Kajukenbo

TL;dr -

Consider using ex. Since you want the front of a given line, then the syntax is basically the same as what you might find for sed but the option of "in place editing" is built-in.

I cannot imagine an environment where you have sed but not ex/vi, unless it is a MS Windows box with some special "sed.exe", maybe.

sed & grep sort of evolved from ex / vi, so it might be better to say sed syntax is the same as ex.

You can change the line number to something besides #1 or search for a line and change that one.

source=myFile.txt
Front="This goes IN FRONT "
man true > $source
ex -s ${source} <<EOF
1s/^/$Front/ 
wq
EOF
$ head -n 3 $source
This goes IN FRONT TRUE(1)                                                    User Commands                                                    TRUE(1)

NAME

Long version, I recommend ex (or ed if you are one of the cool kids).

I like ex because it is portable, extremely powerful, allows me to write in-place, and/or make backups all without needing GNU (or even BSD) extensions.

Additionally, if you know the ex way, then you know how to do it in vi - and probably vim if that is your jam.

source=myFile.txt
topString="******  This goes on TOP  ******"
man true > "$source"
ex -s ${source} <<-EOF
0r!echo "$topString"
wq
EOF

The 0r is shorthand for :0read! or :0r! that you have likely used in vi mode (it is literally the same thing) but the : is optional here.
Writing to the special line #0 automatically pushes everything "down", and then you just :wq to save your changes.

$ head -n 5 "$source"
******  This goes on TOP  ******
TRUE(1)                                                    User Commands                                                    TRUE(1)

NAME
       true - do nothing, successfully

Also, some older sed implementations do not seem to have extensions (like \U&) that ex should have by default.