Write string to a file without a shell
If you know of any other non-empty file on the system, then with POSIX sed
:
sed -e 's/.*/hello world/' -e 'wtarget' -e q otherfile
With GNU sed
and just your own non-empty file, you can use:
sed -i.bak -e '$ihello world' -e 'd' foo
With BSD sed
, this would work instead:
sed -i.bak -e '$i\
hello world' -e d foo
If you're not using a shell then presumably the linebreak isn't an issue.
With ex
, if the target file exists:
ex -c '0,$d' -c 's/^/hello world/' -c 'x' foo
This just deletes everything in the file, replaces the first line with "hello world", then writes and quits. You could do the same thing with vi
in place of ex
. Implementations are not required to support multiple -c
options, but they generally do. For many ex
implementations the requirement that the file already exist is not enforced.
Also with awk
:
awk -v FN=foo -v STR="hello world" 'BEGIN{printf(STR) > FN }'
will write "hello world" to file "foo".
If there are existing files containing the bytes you want at known locations, you can assemble a file byte by byte over multiple commands with dd
(in this case, alphabet
contains the alphabet, but it could be a mix of input files):
dd if=alphabet bs=1 skip=7 count=1 of=test
dd if=alphabet bs=1 skip=4 count=1 seek=1 of=test
dd if=alphabet bs=1 skip=11 count=1 seek=2 of=test
dd if=alphabet bs=1 skip=11 count=1 seek=3 of=test
dd if=alphabet bs=1 skip=14 count=1 seek=4 of=test
cat test
hello
From there, just regular cp
will work, or you might have been able to put it in-place to start with.
Less commonly, the mispipe
command from moreutils allows constructing a shell-free pipe:
mispipe "echo 1" "tee wtarget"
is equivalent to echo 1 | tee wtarget
, but returning the exit code of echo
. This uses the system()
function internally, which doesn't strictly require a shell to exist.
Finally, perl
is a common command and will let you write arbitrary programs to do whatever you want on the command line, as will python
or any other common scripting language. Similarly, if a shell just isn't "running", but it does exist, sh -c 'echo 1 > target'
will work just fine.
You could use awk
:
awk 'BEGIN {print "Hello" > "/tmp/file"}'
When an awk
program consists of only a BEGIN
statement, it just interprets that statement and doesn't process any input.
You can even parameterise this
awk -v text="World" -v file="/tmp/main" 'BEGIN {print text > file}'
Though with that syntax, you'd need to escape backslash characters. Using ARGV
doesn't have the problem:
awk 'BEGIN {print ARGV[1] > ARGV[2]}' World /tmp/main