I am trying to execute specific line of text file in bash. I was wondering is it possible to do that without copying that line in a new file and executing it?
5 Answers
Building on @kev's answer (without piping to a new bash
): Take his script source
script.sh
echo hello
echo world
echo foo
echo bar
and execute a certain line inside a $(...)
:
command:
$(sed -n '3p' script.sh)
It will print foo
in bash your current shell.
-
@ormaaj: I agree with you in discouraging such a concept. On the other hand it comes handy when following instructions from a simple diary (log) file.– fheubJul 13, 2012 at 9:49
-
-
1@fheub: This isn't a good answer because the argument list is built by word-splitting, with pathname expansion enabled. This will fail in all but simple cases. Always quote command substitutions. kev's answer is ok. Mine works in either a child process or the current environment because of the
eval
(and uses properly escaped input). If you have GNU sed available, a shell command can be executed in pure sed using thee
function:sed -n $'3{e\np;q}' file
– ormaajJul 13, 2012 at 10:15 -
@ormaaj: Touché! But Vladimir seems to be looking for a KISS-solution.– fheubJul 13, 2012 at 10:27
script.sh
echo hello
echo world
echo foo
echo bar
command:
sed -n '3p' script.sh | bash
It will print foo
-
I think you should pipe to
bash
as Vladimir wants to execute the line.– BernhardJul 13, 2012 at 7:27
If you are talking about a shell script, you could do that like in this example:
#!/bin/bash
LINE='less'
#...
LINE="$LINE myfile.txt"
# now we execute that:
$LINE
Another example includes capturing the output for further processing:
#!/bin/bash
LINE="grep foo bar.txt"
OUTPUT=$($LINE)
In the second example, the variable $OUTPUT
then contains the results of the executed grep
command.
Probably not quite what you want, but could be useful to mention:
history -r script.sh
This reads each line of the script into your history list, from which you can execute any line using bash
's history expansion feature.
A few failed attempts at reading in just the line you want to execute:
# No command substitution; the argument is added literally
history -s $(sed -n '3p' script.sh)
# Can't seem to read from stdin or from a process substitution
sed -n '3p' script.sh | history -r -
sed -n '3p' script.sh | history -r /dev/stdin
history -r <( sed -n '3p' script.sh )
You could but probably shouldn't. Create a library containing functions and source it. This is a very unusual requirement. Bash has actual means of control flow - there's no need to grab strings of code out of files to execute. This can be dangerous.
Here's a "toy" example, not for production use...
#!/usr/bin/env bash
shopt -s extglob
# evalLine filepath line-number
evalLine() {
[[ -f $1 && $2 == +([[:digit:]]) ]] || return 1
local x
# This subtraction compensates for a bug that's since been fixed in 4.2.35
mapfile -ts $(($2 - 1)) -n 1 x &&
${x:+eval "$x"}
} <"$1"
evalLine /dev/stdin "$1" <<"EOF"
echo "line 1"
f() { echo "$1"; }; f "line 2"
var3='line 3' eval 'echo "$var3"'
EOF
Late edit: Note the comment regarding a bug in mapfile
.