19

Sorry for the confusing title!

Suppose I run

apt-cache depends kde-window-manager > ~/Desktop/kwin-depends

I'll get a file named "kwin-depends" in my Desktop folder.

Is there some trick to include the command I issued as part of the file, preferably at the start of the file?

So, at least in 14.04 LTS, the first few lines would look like this:

apt-cache depends kde-window-manager > ~/Desktop/kwin-depends

kde-window-manager
  Depends: kde-runtime
  Depends: libc6
 |Depends: libegl1-mesa
  Depends: <libegl1-x11>

Instead of just like this:

kde-window-manager
  Depends: kde-runtime
  Depends: libc6
 |Depends: libegl1-mesa
  Depends: <libegl1-x11>
DK Bose
  • 42,548
  • 23
  • 127
  • 221
  • 3
    As there are now many other good and flexible solutions, you should maybe consider to unaccept the answer which suggests to manually write the command to the file by typing it twice and accept one of the versatile solutions instead. – Byte Commander Oct 22 '15 at 19:50

9 Answers9

19

I would just use a simple function. Add this to your ~/.bashrc file:

function runcom(){
    echo "$@"
    ## Run the command
    $@
}

Now, whenever you want to run a command and print it, you can do:

runcom apt-cache depends kde-window-manager > out

The above produces this file:

$ cat out
apt-cache depends kde-window-manager
kde-window-manager
  Depends: perl
  Depends: kde-runtime
  Depends: kde-style-oxygen
  Depends: libc6
 |Depends: libegl1-mesa
  Depends: <libegl1-x11>
    libegl1-mesa
  Depends: libgcc1
 |Depends: libgl1-mesa-glx
  Depends: <libgl1>
    libgl1-mesa-swx11
    libgl1-mesa-glx
 |Depends: libgles2-mesa
  Depends: <libgles2>
    libgles2-mesa
  Depends: libice6
  Depends: libkactivities6
  Depends: libkcmutils4
  Depends: libkdeclarative5
  Depends: libkdecorations4abi2
  Depends: libkdecore5
  Depends: libkdeui5
  Depends: libkio5
  Depends: libknewstuff3-4
  Depends: libkwineffects1abi5
  Depends: libkwinglesutils1
  Depends: libkwinglutils1abi2
  Depends: libkworkspace4abi2
  Depends: libplasma3
  Depends: libqt4-dbus
  Depends: libqt4-declarative
  Depends: libqt4-script
  Depends: libqtcore4
  Depends: libqtgui4
  Depends: libsm6
  Depends: libstdc++6
  Depends: libwayland-client0
 |Depends: libwayland-egl1-mesa
  Depends: <libwayland-egl1>
    libwayland-egl1-mesa
  Depends: libx11-6
  Depends: libx11-xcb1
  Depends: libxcb-composite0
  Depends: libxcb-damage0
  Depends: libxcb-image0
  Depends: libxcb-keysyms1
  Depends: libxcb-randr0
  Depends: libxcb-render0
  Depends: libxcb-shape0
  Depends: libxcb-shm0
  Depends: libxcb-sync1
  Depends: libxcb-xfixes0
  Depends: libxcb-xtest0
  Depends: libxcb1
  Depends: libxcursor1
  Depends: libxext6
  Depends: libxrandr2
  Depends: libxxf86vm1
  Breaks: kde-style-bespin
  Breaks: kde-style-bespin:i386
  Breaks: <kde-style-skulpture>
  Breaks: <kde-style-skulpture:i386>
  Breaks: kde-workspace-data
  Breaks: <kde-workspace-data:i386>
  Breaks: kdeartwork-theme-window
  Breaks: kdeartwork-theme-window:i386
  Breaks: <kdebase-workspace-data>
  Breaks: <kdebase-workspace-data:i386>
  Breaks: kwin-style-crystal
  Breaks: kwin-style-crystal:i386
  Breaks: kwin-style-dekorator
  Breaks: kwin-style-dekorator:i386
  Breaks: kwin-style-qtcurve
  Breaks: kwin-style-qtcurve:i386
  Replaces: kde-workspace-data
  Replaces: <kde-workspace-data:i386>
  Replaces: <kdebase-workspace-data>
  Replaces: <kdebase-workspace-data:i386>
  Conflicts: kde-window-manager:i386
terdon
  • 100,812
  • I don't mean to move the goalposts here, but is there a way to get your code to accept aliases as well? For example, if I alias apt-cache depends to acd, I get "No command 'acd' found, did you mean: ..." when I run runcom acd leafpad > out. – DK Bose Oct 23 '15 at 12:58
  • @DKBose aliases are defined in .bashrc file, not in shell, and functions call only the binary files that are located under $PATH variable . But you can do a simple trick. For instance, ls is aliased to ls --color=auto' in reality. What you could do (as long as there is no single quotes or double quotes in your alias ), is this : $ terdonsFunction $(alias ls | awk -F '=' '{$1="";print}'| tr "'" " "). – Sergiy Kolodyazhnyy Oct 24 '15 at 10:14
  • OR turn your command into a variable. Like I showed in my answer before. MYCOMMAND="apt-cache depends"; terdonsFunction $MYCOMMAND leafpad > out.txt – Sergiy Kolodyazhnyy Oct 24 '15 at 10:19
  • @Serg, please see GARCIN David's answer: http://askubuntu.com/a/688936/248158 – DK Bose Oct 24 '15 at 10:40
  • This is the "obvious" answer and works great unless any of the arguments involve code that executes and has side effects which might accumulate. This is an edge case, so it wouldn't occur very often. – Joe Oct 30 '15 at 07:25
  • @Joe like what? I can't, off the top of my head, think of any code that would run with no issues if executed directly but would cause problems if run this way. I'm not saying there isn't any, I'm just wondering if you could give an example. – terdon Oct 30 '15 at 12:18
  • I thought this would be easier, but it's kind of convoluted. Here's a small test script along the lines of what I was thinking, but I'm not sure it proves anything. http://dl.dropbox.com/u/54584985/mytest_side_effects – Joe Oct 30 '15 at 23:58
  • @Joe well yes, that would have the same problem even if you just manually echoed the same "command" before running it. If you bring command substitution into the mix, you are, by definition, running the command twice. – terdon Oct 31 '15 at 00:37
11

Most minimalistic - approach #4 and #3, both could be converted into function; #2 my favorite - awk. #1 uses script command - very versatile tool, useful for recording command line in general; applicable anywhere, for whatever you want to record.

Approach #1: There is a /usr/bin/script command ( which comes with ubuntu by default ) for recording command-line output, which captures everything , along with the prompt and command. To just save one command and its output to specific file, use -c flag and specify output file. Example

xieerqi:$ script -c 'apt-cache depends gnome-terminal' outputFile.txt
Script started, file is outputFile.txt
gnome-terminal
  Depends: gconf-service
    gconf-service:i386
  Depends: libatk1.0-0
  Depends: libc6
  Depends: libgconf-2-4
  Depends: libgdk-pixbuf2.0-0
     (extra output omitted)
Script done, file is outputFile.txt

xieerqi:$ cat outputFile.txt                                              
Script started on 2015年10月22日 星期四 08时58分46秒
gnome-terminal
  Depends: gconf-service
    gconf-service:i386
  Depends: libatk1.0-0
  Depends: libc6
  Depends: libgconf-2-4
  (extra output omitted)

Script done on 2015年10月22日 星期四 08时58分46秒

Approach #2: awk hackery

Awk has system() function which allows you running shell commands from awk script or command. The output will show up on the screen , command first, output next. To redirect what you see to a file use > operator.

That can be done in two ways - ask user to input stuff from stdin or as command line argument. First one is easier to achieve, hence posting that.

(1) awk 'BEGIN{ print "Enter command to run: "; getline com < "/dev/stdin"; system(com) }'

 awk 'BEGIN{ print "Enter command to run: "; getline com < "/dev/stdin"; system(com) }'
Enter command to run: 
apt-cache depends gnome-terminal
gnome-terminal
  Depends: gconf-service
    gconf-service:i386
  Depends: libatk1.0-0
  Depends: libc6
  Depends: libgconf-2-4
  Depends: libgdk-pixbuf2.0-0
  Depends: libglib2.0-0 
  (extra output omitted)

(2) Command line args version; not including output to avoid making answer too long. Again, append > to redirect to file

awk 'BEGIN{for (i=1; i<= ARGC; i++) myString = myString"  "ARGV[i]; print myString; system(myString)  }' apt-cache depends gnome-terminal

Approach #3: ask bash to do the job for you

xieerqi@eagle:~$ bash -c ' MYCOMMAND="apt-cache depends gnome-terminal"; echo $MYCOMMAND ; $MYCOMMAND    '
apt-cache depends gnome-terminal
gnome-terminal
  Depends: gconf-service
    gconf-service:i386
  Depends: libatk1.0-0
  Depends: libc6
  Depends: libgconf-2-4
  Depends: libgdk-pixbuf2.0-0
  Depends: libglib2.0-0

Redirect to file with > operator:

bash -c ' MYCOMMAND="apt-cache depends gnome-terminal"; echo $MYCOMMAND ; $MYCOMMAND ' > output.txt

Approach #4:(my second favorite)

Inspired by ByteCommander's post; we can use read and then run necessary commands in subshell

read command && (printf "COMMAND: %s" "$command";printf "\n+++++++\n"; sh -c "$command")

Sample run:

xieerqi:$ read command && (printf "COMMAND READ: %s" "$command";printf "\n+++++++\nOUTPUT\n"; sh -c "$command")                                       
printf "This was a triumph; I'm making a note here - huge success"
COMMAND READ: printf "This was a triumph; I'm making a note here - huge success"
+++++++
OUTPUT
This was a triumph; I'm making a note here - huge success

Approach #5:

Use echo or here string (aka <<< "string" ) to provide arguments to sh -c through xargs

xieerqi:$ echo "apt-cache policy gnome-terminal" | xargs -I {} bash -c 'echo {}; {}'                                                            
apt-cache policy gnome-terminal
gnome-terminal:
  Installed: 3.6.2-0ubuntu1
  Candidate: 3.6.2-0ubuntu1
  Version table:
 *** 3.6.2-0ubuntu1 0
        500 http://us.archive.ubuntu.com/ubuntu/ trusty/main amd64 Packages
        100 /var/lib/dpkg/status

And if you want , you can use this same trick with an alias:

xieerqi:$ printAndRun <<< "apt-cache policy gnome-terminal"                                                                                     
apt-cache policy gnome-terminal
gnome-terminal:
  Installed: 3.6.2-0ubuntu1
  Candidate: 3.6.2-0ubuntu1
  Version table:
 *** 3.6.2-0ubuntu1 0
        500 http://us.archive.ubuntu.com/ubuntu/ trusty/main amd64 Packages
        100 /var/lib/dpkg/status

xieerqi:$ type printAndRun
printAndRun is an alias for 'xargs -I {} bash -c "echo {}; {}"'
Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • Nice, but it doesn't include the command the way Arronical's code does. – DK Bose Oct 22 '15 at 15:31
  • @DKBose I'll add another approach which will include the command. Five minutes – Sergiy Kolodyazhnyy Oct 22 '15 at 15:36
  • @DKBose how's my approach #2 ? – Sergiy Kolodyazhnyy Oct 22 '15 at 15:52
  • That's clever, awk really is a very configurable little fella ain't he! `script looks handy for a few other uses too. – Arronical Oct 22 '15 at 16:00
  • @Arronical yes, awk is really versatile. Oh, by the way, adding version with command line arguments in a second. As for script I have used it extensively to record output for my college homework , record output for sending it to my friends, etc. Whoever came up with this program is a genius – Sergiy Kolodyazhnyy Oct 22 '15 at 16:05
  • It's going to help so much when doing site visits. I won't have to write down everything I've done in a little book anymore! You've already got my +1 – Arronical Oct 22 '15 at 16:07
  • I'm sorry to be a pest but none of the methods include the command at the top of the file created by redirection. Arronical's way does. – DK Bose Oct 22 '15 at 16:58
  • @DKBose look at the output of Approach #3 and #2, they include line apt-cache depends gnome-terminal; that's the command, and if you redirect that to a file , whatever you see on the screen (including those lines), it will all be in the file out output to – Sergiy Kolodyazhnyy Oct 22 '15 at 17:03
  • @Serg I really like the 3rd one. You can also add >~/Desktop/whateverfile at the end of it and it works! +1 buddy! – Terrance Oct 22 '15 at 17:05
  • @Terrance I've left the redirection part out, thought it was obvious, but seems like it's not. ^_^ Thank you for the kind words , by the way! – Sergiy Kolodyazhnyy Oct 22 '15 at 17:12
  • 1
    This is the best answer because using script avoids any side effects from arguments which might execute code when displayed with echo, etc. - making the second, intended, execution possibly give different results than if the command was run separately. – Joe Oct 30 '15 at 07:37
11

You can do:

tee file.txt <<<'apt-cache depends kde-window-manager' | bash >>file.txt

Same thing using echo instead of Here strings (<<<):

echo 'apt-cache depends kde-window-manager' | tee file.txt | bash >>file.txt
  • tee will write to STDOUT and also to the file file.txt

  • The STDOUT of tee i.e. apt-cache depends kde-window-manager will be fed to bash to run the command and append the STDOUT to file.txt.

Example:

$ echo 'apt-cache depends kde-window-manager' | tee file.txt | bash >>file.txt

$ cat file.txt 
apt-cache depends kde-window-manager
kde-window-manager
  Depends: kde-runtime
  Depends: libc6
 |Depends: libegl1-mesa
  Depends: <libegl1-x11>
heemayl
  • 91,753
6
  1. Start script -q outputfile
  2. Execute your command
  3. Press Ctrl-D
  4. Open the file outputfile

Example

Start script

[aboettger:~/tmp] % script -q ~/Desktop/kwin-depends

Start your command

[aboettger:~/tmp] % apt-cache depends kde-window-manager
<kde-window-manager>
[aboettger:~/tmp] % 

Press Ctrl-D

Script done, file is /home/aboettger/Desktop/kwin-depends

Show your command and output

[aboettger:~/tmp] % cat ~/Desktop/kwin-depends

and you will see something like this

[aboettger:~/tmp] % apt-cache depends kde-window-manager
<kde-window-manager>
A.B.
  • 90,397
5

If you want alias expansion (bash only) you can do it this way :

function runcmd
{
    local check_cmd=${BASH_ALIASES[$1]}

    if [ -z "$check_cmd" ]; then
        check_cmd=$1
    fi

    shift #skip 1st arg

    echo "$check_cmd $@"
    $check_cmd $@
}

You can now run

runcmd acd leafpad > out
4

There may be an easier way, but you can do it by a script:

#!/bin/sh
echo $1
apt-cache depends $1

Create a file script with this content in your Home folder and give execution permission

chmod +x script

Run it this way:

./script kde-window-manager > ~/Desktop/kwin-depends
Pilot6
  • 90,100
  • 91
  • 213
  • 324
  • This approach has the advantage of making it easy to repeat that command line later if you want to! You can also write the redirection into the script, so that script.sh always creates a file called script.log with its output. – Gaurav Oct 23 '15 at 01:21
4

Extremely simple solution using a one-line Bash function

Preparation:

This approach uses a custom bash function to provide the desired functionality. You define it by executing the following line in your terminal session. note that you may chose any valid bash variable name instead of runandlog:

runandlog () ( IFS=' '; printf "[%s] $ %s\n%s\n" "$USER" "${*:2}" "$("${@:2}")" | tee -a "$1" | tail -n +2; )

This however only persists for the current Bash session, that means after closing the terminal window, the function will be gone.
If you tried and liked it though, you can make it always available by editing your ~/.bashrc file and appending this line to the end of it.

How to use:

After having defined the function, you may use it to run commands while logging both the command itself and its output to a file. You could even add more information like the user who executed it, which I already included into the function, or exact time it was ran. Feature-requests in comments are welcome! :)

The syntax is simple:

runandlog LOGFILENAME YOUR-COMMAND-WITH-ARGUMENTS

Example:

An example session as user bytecommander, operating from the home directory could look like this:

bytecommander: ~ $  runandlog mylogfile fortune
A mathematician is a device for turning coffee into theorems.
        -- P. Erdos

Which will result in a new file mylogfile (if it already exists, the function will append the output to it!) in the current directory with the content:

[bytecommander] $ fortune 
A mathematician is a device for turning coffee into theorems.
        -- P. Erdos
Byte Commander
  • 107,489
3

A fairly unclever but functional trick would be:

(echo "apt-cache depends kde-window-manager" && apt-cache depends kde-window-manager) > ~/Desktop/kwin-depends

Ugly, but it works!

Arronical
  • 19,893
  • I'm accepting this answer because the solution is more broadly applicable. – DK Bose Oct 22 '15 at 14:50
  • @DKBose You will have to type the module twice. But the solution with a script is really universal. – Pilot6 Oct 22 '15 at 16:10
  • I'm sorry that I have to unaccept this answer even though it does what I asked for. I hope you don't mind! – DK Bose Oct 23 '15 at 12:44
  • 2
    Not a problem @DKBose, I knew it was a fairly inelegant solution as I submitted the answer, and have learned some good stuff from the alternative answers posted. – Arronical Oct 26 '15 at 09:45
2

You could simply pass the command to a function which will print the command first and the command's output afterwards (the redirections are kept out of the printed command intentionally, you may easily change this by removing the quotes from the command and by printing and running $@ instead of $1 in the function):

function myfunction() {
    printf "%s\n\n" "$1"
    $1
}
$ myfunction "printf \"bar\n\"" > foo
$ cat foo
printf "bar\n"

bar

To add the command afterwards you could run this command, which will insert the last command run at the top of a file:

<<<"$(<foo)" cat <(history 2 | sed -n '1s/  [0-9][0-9]*  \(.*\)/\1\n/p') - >foo
  • <<<"[...]": here string; [...] is redirected to cat's stdin;
  • $(<foo): command substitution; it's replaced with "foo"'s content;
  • cat [...] - >foo: concatenates stdin to [...] and outputs to "foo";
  • <([...]): process substitution: it's replaced with a file descriptor containing the output of [...];
  • history 2 | sed -n '1s/ [0-9][0-9]* \(.*\)/\1\n/p': outputs the last two commands, removes two spaces followed by one or more digits followed by two spaces from the first line and prints it;
$ printf "bar\n" >foo
$ <<<"$(<foo)" cat <(history 2 | sed -n '1s/  [0-9][0-9]*  \(.*\)/\1\n/p') - >foo
$ cat foo
printf "bar" >foo

bar
kos
  • 35,891
  • Why are you only printing $1? And there's no need for eval. – terdon Oct 23 '15 at 11:33
  • @terdon Well, the idea was to keep the redirections out of the printed command, since I thought this might have looked nicer in OP's case. Nontheless if I change it now it would be identical to your answer. However yes, eval is not needed, not sure why I added it before. Thanks. – kos Oct 23 '15 at 11:46