Copy pwd straight from keyboard - terminal

I looked for a way to copy the current straight to the keyboard and found it in another topic. Now I have in my .bash:
alias pwd='pwd | tr -d '\n' | pbcopy
but when now when I type the command, it directly execute it. So for example if I type:
cp -r <Command-V>
it directly reads in my command without me being able to add anything after it. How do I fix this? Thanks

Related

How do I open a file in VS Code terminal by partially matching the file name?

If I have a file named w5_align_example.cpp, how do I open that file in VS Code integrated terminal by only supplying the word align?
code w5_align_sample.cpp would open it but I sometimes only remember the keyword align unless I search in a separate command to see what the file begins with. I want to open in a single command instead.
I've tried:
$ ls | grep "align" | code which gives me Run with 'code -' to read output from another program (e.g. 'echo Hello World | code -'). error.
$ ls | grep "align" | code - opens up a new file called code-stdin-sfd.txt with the text w5_align_example.cpp inside.
What would be the simplest (i.e. shortest) command to do this?
ls | grep "align" | xargs -I{} code {}
or
code $(ls | grep "align")
You can just use *. It matches any string and can be used multiple times.
code *align*
In some shells, you can combine this with tab completion. Just type:
code *align*
And then press Tab. This will fill in the rest of the file name, but it will beep if there is more than one option.

Paste the last output and edit it in bash

I like to use bash (on linux) without touching mouse.
I often encounter the following situation.
$ locate libfreetype.a
/usr/lib/x86_64-linux-gnu/libfreetype.a
$ cd /usr/lib/x86_64-linux-gnu
In this case, I copy /usr/lib/x86_64-linux-gnu/ and paste it using mouse or type it. I do not want to do that.
Ideally, the output of locate libfreetype.a is stored in somewhere (maybe in killring??) and paste it with C-y command and edit it on terminal.
Are there good way to do this?
(Just for this example case, there are smart one-line commands. But those are not the desired answers. I want a general solution.)
Another example
Suppose that I remember that there is a memo... in the same directory as libfreetype.a but I forgot the directory name.
$ locate libfreetype.a
/usr/lib/x86_64-linux-gnu/libfreetype.a
$ nano /usr/lib/x86_64-linux-gnu/memo # Tab completion here
$ nano /usr/lib/x86_64-linux-gnu/memo_xxx.txt
if I could cache the output /usr/lib/x86_64-linux-gnu/libfreetype.a and paste it, things are very easy.
(nano $(dirname $(locate libfreetype.a))/memo_xxx.txt works for this case, but if I want to change the path itself, I need to think another technique.)
As noted in the comments, there probably no common way to do this in terminal. But it's possible to redirect the output of the command to program that copy stdin to clipboard, e. g. xclip. If you want to insert and edit copied text in terminal, you need to remove newline characters before copying. Consider following script:
copy.bash
#!/bin/bash
tr '\n' ' ' | xclip
Usage:
$ locate libfreetype.a | copy
$ cd # now press <shift> + <insert>
$ cd /usr/lib/x86_64-linux-gnu/libfreetype.a # continue editing
The xclip command copies its input for pasting into X applications.
The tr '\n' ' ' command translates all newlines into spaces. You need this if you want to paste the text into command line. It strips the trailing newline and joins lines if output contains more than one. If use plain xclip all newline characters are pasted literally, which causes bash to run command immediately after pasting and doesn't allow to edit it.
If output of the command (e. g. locate) is multi-line and you want to choose only one of them to copy (instead of copying all), you can use iselect. iselect reads input and shows the interactive menu for selecting a line/lines and prints it to the standart output.
Use it like this:
$ locate pattern | iselect -a | tr '\n' ' ' | xlip
# locate prints several lines
# iselect allows user to select one line interactively
# the result is copied to clipboard
$ # <shift> + <insert>
This also can be a script:
icopy.bash
#!/bin/bash
iselect -am | tr '\n' ' ' | xclip
(the -m option allows to choose several lines instead of one)
Usage:
$ locate pattern | icopy
Disadvantages of these approaches:
it works only with X sessions since xlcip need the X session to be running
you need to install new software (xclip and, optionally, iselect)
you need to redirect output explicitly, before running the command; so, technically, it cannot be considered as answer. But it is the best solution I have found for myself.
BTW, here is the script on my local machine that I really use quite often:
$ cat ~/bin/copy
#!/bin/bash
paste -sd\ | tr -d '\n' | xsel --clipboard
echo "Copied: $(xsel --clipboard --output)" >&2
$ echo hello | copy
Copied: hello
Links: man iselect, man xclip, man tr, yank.
You can run script (man script) from your .bashrc which generates a live log of your session's output. And bind a shortcut for opening the log file in an editor, so you can insert yanked text back into $READLINE_LINE.
But script captures raw output from interactive programs (such as editors), so if script could be modified to skip interactive output, it would work. Next step would be parsing output, to make navigation faster.
Here is a .bashrc snippet that does this for non-interactive tools only: https://asciinema.org/a/395092
I noticed that a solution to this problem is given by a terminal emulator kitty. We can use a feature called "hints" and keyboard shortcuts configured by default.
As in the original question, let's think of the situation.
$ locate libfreetype.a
/usr/lib/x86_64-linux-gnu/libfreetype.a
$ # you want to input /usr/lib/x86_64-linux-gnu here
If you are using kitty, you can type ctrl+shift+p and then l.
You will enter a mode to select a line from the screen. When you can select the previous line, it is pasted into the current terminal input.
The detail if found in the official documentation.
The configuration associated with the action is written like this.
map ctrl+shift+p>l kitten hints --type line --program -
This means that kitten hints --type line --program - is the command mapped from ctrl+shift+p followed by l.
you could use
!$
like
shell$ echo myDir/
myDir/
shell$ cd !$
cd myDir/
shell$ pwd
/home/myDir

Changing file extensions for all files in a directory on OS X

I have a directory full of files with one extension (.txt in this case) that I want to automatically convert to another extension (.md).
Is there an easy terminal one-liner I can use to convert all of the files in this directory to a different file extension?
Or do I need to write a script with a regular expression?
You could use something like this:
for old in *.txt; do mv $old `basename $old .txt`.md; done
Make a copy first!
Alternatively, you could install the ren (rename) utility
brew install ren
ren '*.txt' '#1.md'
If you want to rename files with prefix or suffix in file names
ren 'prefix_*.txt' 'prefix_#1.md'
Terminal is not necessary for this... Just highlight all of the files you want to rename. Right click and select "Rename ## items" and just type ".txt" into to the "Find:" box and ".md" into the "Replace with:" box.
The preferred Unix way to do this (yes, OS X is based on Unix) is:
ls | sed 's/^\(.*\)\.txt$/mv "\1.txt" "\1.md"/' | sh
Why looping with for if ls by design loops through the whole list of filenames? You've got pipes, use them. You can create/modify not only output using commands, but also commands (right, that is commands created by a command, which is what Brian Kernighan, one of the inventors of Unix, liked most on Unix), so let's take a look what the ls and the sed produces by removing the pipe to sh:
$ ls | sed 's/^\(.*\)\.txt$/mv "\1.txt" "\1.md"/'
mv "firstfile.txt" "firstfile.md"
mv "second file.txt" "second file.md"
$
As you can see, it is not only an one-liner, but a complete script, which furthermore works by creating another script as output. So let's just feed the script produced by the one-liner script to sh, which is the script interpreter of OS X. Of course it works even for filenames with spaces in it.
BTW: Every time you type something in Terminal you create a script, even if it is only a single command with one word like ls or date etc. Everything running in a Unix shell is always a script/program, which is just some ASCII-based stream (in this case an instruction stream opposed to a data stream).
To see the actual commands being executed by sh, just add an -x option after sh, which turns on debugging output in the shell, so you will see every mv command being executed with the actual arguments passed by the sed editor script (yeah, another script inside the script :-) ).
However, if you like complexity, you can even use awk and if you like to install other programs to just do basic work, there is ren. I know even people who would prefer to write a 50-lines or so perl script for this simple every-day task.
Maybe it's easier in finder to rename files, but if connected remotely to a Mac (e.g. via ssh), using finder is not possible at all. That's why cmd line still is very useful.
Based on the selected and most accurate answer above, here's a bash function for reusability:
function change_all_extensions() {
for old in *."$1"; do mv $old `basename $old ."$1"`."$2"; done
}
Usage:
$ change_all_extensions txt md
(I couldn't figure out how to get clean code formatting in a comment on that answer.)
No need to write a script for it just hit this command
find ./ -name "*.txt" | xargs -I '{}' basename '{}' | sed 's/\.txt//' | xargs -I '{}' mv '{}.txt' '{}.md'
You do not need a terminal for this one; here is a sample demonstration in MacOS Big Sur.
Select all the files, right-click and select "rename..."
Add the existing file extension in "Find" and the extension you want to replace with "Replace with".
And done!
I had a similar problem where files were named .gifx.gif at the end and this worked in OS X to remove the last .gif:
for old in *.gifx.gif; do
mv $(echo "$old") $(echo "$old" | sed 's/x.gif//');
done
cd $YOUR_DIR
ls *.txt > abc
mkdir target // say i want to move it to another directory target in this case
while read line
do
file=$(echo $line |awk -F. '{ print $1 }')
cp $line target/$file.md // depends if u want to move(mv) or copy(cp)
done < abc
list=ls
for file in $list
do
newf=echo $file|cut -f1 -d'.'
echo "The newf is $newf"
mv $file $newf.jpg
done

Bash: piped argument to open command fails. Open commands excutes too early?

I'm pretty much a novice to shell scripting. I'm trying to send the output of some piped commands to an open command in bash in OSX.
My ultimate goal is to compile a Flex/Actionscript application from TextWrangler by calling a bash script with a little Applescript and have the result played directly in a Flash Player. The Applescript is pretty much doing it's job. But the bash script doesn't work as I expect. Same results when I ommit the Applescript and simply put it directly in terminal.
This is what the Applescript is sending to terminal:
mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//' | open -a 'Flash Player'
So basically, I read the last line of the output of mxmlc, which usually looks something like this:
/Users/fireeyedboy/Desktop/DocumentClass.swf (994 bytes)
and I strip everything after the first space it encounters. I know it's hardly bulletproof yet, it's still just a proof of concept. When I get this roughly working I'll refine. It returns the desired result so far:
/Users/fireeyedboy/Desktop/DocumentClass.swf
But as you can see, I then try to pipe this sed result to the Flash Player and that's where it fails. The Flash Player seems to open way too early. I would expect the Flash Player to open only after the script finished the sed command. But it opens way earlier.
So my question is twofold:
Is it even possible to pipe an
argument to the open command this
way?
Do I need to use some type
of delay command to get this
working, since the open command doesn't seem to be waiting for the input?
You're trying to give the name of the swf file as input to stdin of the open command, which it doesn't support.
It expects the file name as an argument (similar to -a).
You can do something like this:
FILENAME=`xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
open -a 'Flash Player' $FILENAME
or on a single line:
open -a 'Flash Player' `xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
If you're using bash (or another modern POSIX shell), you can replace the pretty unreadable backtick character with $( and ):
open -a 'Flash Player' $(xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
All commands in a pipe are started at the same time. During this step, their input/outputs are chained together.
My guess is that open -a 'Flash Player' doesn't wait for input but simply starts the flash player. I suggest to try to run the player with an argument instead:
name=$(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
open -a 'Flash Player' "$name"
I'm not familiar with the "open" command as it seems to be a mac thing, but i think what you want to do is:
open -a 'Flash Player' $(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
In general you can't pipe arguments to a command, you have to specify that you want the output of the previous command to be treated as arguments, either as in my example or with the xargs command. Note that there is a limit on the maximum size of a command line, though.

Is there a way to click a link in Firefox and open a file in an existing VIM session?

I know it's possible to open links in an html page (let's say, if you're using Firefox) with TextMate if the link has this format:
View
But is it possible to do a similar thing with VIM? Perhaps like so:
View
Ideally this would use an existing VIM session.
Cheers,
Bernie
Found a way to do it:
Add a Protocol handler to Firefox
Open firefox and navigate to about:config
Add the following keys
network.protocol-handler.warn-external.txmt boolean false
network.protocol-handler.external.txmt boolean true
#the last one is the path to the script we're about to create
network.protocol-handler.app.txmt string ~/protocol_handler/prot.sh
# I ended up needing this one as well on another machine, (no idea why)
network.protocol-handler.expose.txmt boolean false
Create the script ~/protocol_handler/prot.sh
Copy and paste the following into the file:
#! /usr/bin/env ruby
file_result = ARGV[0].scan(/file\:\/\/((\w|\/|\.)*).*/)
file_path = file_result[0][0]
line_result = ARGV[0].scan(/\&amp\;line\=(\d*).*/)
if line_result
line = line_result[0][0]
system "gvim --remote-silent +#{line} #{file_path}"
else
system "gvim --remote-silent #{file_path}"
end
Save the file.
Change the file mode to be executable:
$ chmod +x ~/protocol_handler/prot.sh
I'm not sure if you have to restart Firefox or not.
If you actually want to use the "vim://" protocol just change the ending on the network keys from txmt to vim. Since several Rails plugins (rails-footer, namely) out there already use txmt, I just used that to avoid recoding.
Have fun!
Berns
http://www.mozilla.org/projects/netlib/new-handler.html
To get tmxt:// links working with gedit, I had to use a bash script from #Rystraum's related answer instead of the Ruby, ~/bin/txmt_proto.bash:
#!/bin/bash
FILE=$1
FILE=$(echo $FILE | grep -o "file:/\/.\+" | cut -c 8- | sed -e 's/%2F/\//g')
LINE=$(echo $FILE | grep -o "\&line=[0-9]\+")
LINE=$(echo $LINE | grep -o "[0-9]\+")
FILE=$(echo $FILE | grep -o "\(.\+\)\&")
FILE=$(echo $FILE | cut -d'&' -f1)
gedit +$LINE $FILE
and change the Firefox config network.protocol-handler.app.txmt to point at the script:
network.protocol-handler.app.txmt string ~/bin/txmt_proto.bash

Resources