156

I would like to copy the contents of a variable (here called var) into a file.

The name of the file is stored in another variable destfile.

I'm having problems doing this. Here's what I've tried:

cp $var $destfile

I've also tried the same thing with the dd command... Obviously the shell thought that $var was referring to a directory and so told me that the directory could not be found.

How do I get around this?

jahroy
  • 22,322
  • 9
  • 59
  • 108
user1546083
  • 1,777
  • 3
  • 13
  • 12
  • 2
    Please improve your question by posting some [properly formatted](http://stackoverflow.com/editing-help) code that shows how you're populating your variables, and all **relevant** error messages exactly as they appear. – Todd A. Jacobs Jul 23 '12 at 19:07
  • 2
    What do you mean by "copy the contents of a variable" to a directory? Does `$var` specify a file name or some text that should be written to a file? If it specifies text, then what is the name of the file to which you'd like to write this content? – jahroy Jul 23 '12 at 19:11
  • $var contains the text that I want to be copied and the file to which it should be written is user-defined hence the reason that I am using a variable in the first place. – user1546083 Jul 23 '12 at 19:17
  • 2
    Perhaps `$destdir` should be named `$destfile` so it's not misleading... The name `$destdir` suggests it specifies a directory rather than a file. This would make your whole question straightforwar and easy to understand. – jahroy Jul 23 '12 at 19:27
  • Related: https://stackoverflow.com/questions/6790631/use-the-contents-of-a-file-to-replace-a-string-using-sed – Jesse Nickles Aug 07 '23 at 23:23

7 Answers7

199

Use the echo command:

var="text to append";
destdir=/some/directory/path/filename

if [ -f "$destdir" ]
then 
    echo "$var" > "$destdir"
fi

The if tests that $destdir represents a file.

The > appends the text after truncating the file. If you only want to append the text in $var to the file existing contents, then use >> instead:

echo "$var" >> "$destdir"

The cp command is used for copying files (to files), not for writing text to a file.

pb2q
  • 58,613
  • 19
  • 146
  • 147
  • 6
    Needs more quotes -- right now, any runs of whitespace within the variable's value will be converted to a single space, glob expressions will be expanded, etc. – Charles Duffy Jul 23 '12 at 19:54
  • Thanks, @CharlesDuffy I'm always neglecting the quotes, esp. for space chars in filenames – pb2q Jul 23 '12 at 20:02
  • 2
    One other thing -- you need a space between the closing quote and the `]` in `"$destdir"]`. – Charles Duffy Jul 23 '12 at 23:57
  • 4
    `echo` also adds a trailing newline to your variable. – Ben Dilts Jun 11 '15 at 18:08
  • 8
    This fails if `$var` is `-e text to append`, and does't actually write the `-e` – Eric Mar 21 '18 at 23:37
  • 1
    Sorry, I mean `-e` by itself – Eric Mar 21 '18 at 23:56
  • I did use echo command, but it strips out newlines from the string contained in the variable. So this doesn't work in some cases. – Kris May 25 '18 at 01:50
  • 3
    Because `echo $var` is relying on the shell to expand your variable, it doesn't work very will with things like white space and special characters. You can use `perl -e 'print($ENV{var})'` instead, and it will output precisely the value of the variable. – Blake Mitchell May 25 '18 at 21:54
  • This doesn't work when variable value contents many special character. Like in my case I have store shell script content in variable and trying to write it to a file but it is not working. – Ashish Sharma Dec 31 '21 at 13:12
79

echo has the problem that if var contains something like -e, it will be interpreted as a flag. Another option is printf, but printf "$var" > "$destdir" will expand any escaped characters in the variable, so if the variable contains backslashes the file contents won't match. However, because printf only interprets backslashes as escapes in the format string, you can use the %s format specifier to store the exact variable contents to the destination file:

printf "%s" "$var" > "$destdir"

Trebawa
  • 899
  • 6
  • 4
  • 4
    printf is also good if you have newlines in the variable string, like from a triple quote or heredoc. echo doesn't handle the newlines well. – beeflobill Jun 27 '18 at 22:19
  • 1
    @beeflobill can you explain how "echo doesn't handle the newlines well". The only thing I've seen is that echo adds a newline. Which does make `echo "$var" > destfile` wrong. – SensorSmith Sep 28 '18 at 19:58
  • 1
    @SensorSmith In my experiments, I see that when the string has "\n" that echo will print a newline character. But, if the string has an actual newline character that echo will not print it. I don't know why. – beeflobill Oct 04 '18 at 17:46
  • 3
    Basically a portability (and reliability) issue. https://unix.stackexchange.com/a/65819 … or for the deadly among us: `printf` is better, move on. – user3342816 Sep 18 '19 at 18:16
  • 4
    If you, fellow pilgrim, reading this answer and thinking about removing those double quotes around `$var` — stop it. It won't work without quotes or with a single quotes in some cases. Just accept that you have to use double quotes. This knowledge cost me about a day of debugging a broken GitHub Actions Workflow where I saved a private key (with newlines) from a secret to a file… – madhead Feb 09 '21 at 15:43
  • Having issues with either of these solutions. Having this variable: - name: 'fullversion' value: "$(suite.version.number)+$(date:yyyyMMdd)$(rev:.r)", doing printf "%s" "$(fullversion)" > "$(build.artifactstagingdirectory)/version" just prints (e.g.) "2.0.0-rc+". "$(date:yyyyMMdd)$(rev:.r)" is not printed. – Lupa May 19 '22 at 15:09
53

None of the answers above work if your variable:

  • starts with -e
  • starts with -n
  • starts with -E
  • contains a \ followed by an n
  • should not have an extra newline appended after it

and so they cannot be relied upon for arbitrary string contents.

In bash, you can use "here strings" as:

cat <<< "$var" > "$destdir"

As noted in the comment by Ash below, @Trebawa's answer (formulated in the same room as mine!) using printf is a better approach than cat.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 9
    From what I can tell, cat will always insert a newline at the end of the file, regardless of there not being a newline at the end of the variable (although arguably this is a good thing in most cases). The [answer using printf](https://stackoverflow.com/a/49418406/5951320) seems to avoid inserting a trailing newline. – ash Jul 03 '18 at 14:28
  • @Ash another advantage of `printf` over `cat` is that the former is a shell builtin which will always execute faster than an external binary. – Géza Török Feb 06 '20 at 22:50
25

All of the above work, but also have to work around a problem (escapes and special characters) that doesn't need to occur in the first place: Special characters when the variable is expanded by the shell. Just don't do that (variable expansion) in the first place. Use the variable directly, without expansion.

Also, if your variable contains a secret and you want to copy that secret into a file, you might want to not have expansion in the command line as tracing/command echo of the shell commands might reveal the secret. Means, all answers which use $var in the command line may have a potential security risk by exposing the variable contents to tracing and logging of the shell.

For variables that are already exported, use this:

printenv var >file

That means, in case of the OP question:

printenv var >"$destfile"

Note: variable names are case sensitive.

Warning: It is not a good idea to export a variable just for the sake of printing it with printenv. If you have a non-exported script variable that contains a secret, exporting it will expose it to all future child processes (unless unexported, for example using export -n).

Christian Hujer
  • 17,035
  • 5
  • 40
  • 47
  • 4
    This should be the accepted answer. It correctly answers OP's question without potentially dangerous side-effects. Its only drawback is the fact that it isn't a shell built-in even though you'll find it on just about every linux box. I just want to point out that it is case-sensitive. `printenv user` prints nothing while `printenv USER` prints the current username (at least on my computer). – FlippingBinary Dec 24 '20 at 14:33
  • Yes, the environment on Unix (POSIX/Linux) is case sensitive, just like (almost?) everything else. Added a note. Thanks. – Christian Hujer Dec 27 '20 at 17:00
  • 2
    This should be the accepted answer. It also works within "too smart for their own good" escaping routines like the one in Jenkins. – Sergio Jul 06 '21 at 13:38
  • 2
    This would only work for environment variables, not for local script's variables. You'd have to `export` those variables, and if your values contains any secrets that would mean that any processes running as the same users would be able to read those values from the process table and all child processes would also inherit those values, potentially leaking info to other processes that you weren't expecting. Not an ideal situation. Using `printf "$var"` or `foo <<< "$var"` should work in all cases. – Lie Ryan Feb 08 '22 at 05:29
  • Thanks @LieRyan that's a good point, I've updated the answer accordingly. Note that your suggestions potentially leak the variable value to the log when command echo or tracing are enabled. – Christian Hujer Feb 11 '22 at 06:34
8

If I understood you right, you want to copy $var in a file (if it's a string).

echo $var > $destdir
qwertz
  • 14,614
  • 10
  • 34
  • 46
  • This is how it's done. Using the `cp` command will make the shell interpret the first argument as the name of a file or directory. – jahroy Jul 23 '12 at 19:41
  • 7
    If you not enclose `$var` into quotes, content of the file is not 100% equal with the $var's content in some cases. For example when "echoing" vars that contains RSA keys, you get invalid key in the file. – srigi Aug 10 '17 at 10:02
  • 8
    Fails if `$var` contains spaces or `-e` – Eric Mar 21 '18 at 23:55
0

When you say "copy the contents of a variable", does that variable contain a file name, or does it contain a name of a file?

I'm assuming by your question that $var contains the contents you want to copy into the file:

$ echo "$var" > "$destdir"

This will echo the value of $var into a file called $destdir. Note the quotes. Very important to have "$var" enclosed in quotes. Also for "$destdir" if there's a space in the name. To append it:

$ echo "$var" >> "$destdir"
David W.
  • 105,218
  • 39
  • 216
  • 337
0

you may need to edit a conf file in a build process:

echo "db-url-host=$POSTGRESQL_HOST" >> my-service.conf

You can test this solution with running before export POSTGRESQL_HOST="localhost"

Thykof
  • 895
  • 7
  • 9