2

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 5

4

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.

4
  • @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.
    – fheub
    Jul 13, 2012 at 9:49
  • I agree, this is exactly why am I using it. Jul 13, 2012 at 9:55
  • 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 the e function: sed -n $'3{e\np;q}' file
    – ormaaj
    Jul 13, 2012 at 10:15
  • @ormaaj: Touché! But Vladimir seems to be looking for a KISS-solution.
    – fheub
    Jul 13, 2012 at 10:27
2

script.sh

echo hello
echo world
echo foo
echo bar

command:

sed -n '3p' script.sh | bash

It will print foo

1
  • I think you should pipe to bash as Vladimir wants to execute the line.
    – Bernhard
    Jul 13, 2012 at 7:27
1

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.

1

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 )
1

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .