1

I want to pre-pend a directory name to the last word in a line. The line has the following format:

100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0^IoneFile$

where ^I denotes a tab, and $ denotes the end-of-line. This line is generated by git ls-files -s.

I want a sed command to prepend one/ to the filename in this line, like so:

`100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0^Ione/oneFile$

Some of the lines that I've tried, and their corresponding outputs:

  1. Match the longest string of characters that are not \t followed by $; append one/:

    $ git ls-files -s | sed 's|[^\t]*$|one/&|'  
    one/100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0   oneFile
    
  2. Match the longest string of characters that are not \t or ' ' followed by $; pre-pend one/:

    $ git ls-files -s | sed 's|[^\t ]*$|one/&|'
    100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e one/0   oneFile
    
  3. Match the longest string of characters that are not horizontal whitespace, prepend 'one/':

    $ git ls-files -s | sed 's|[^[[:blank:]]]*$|one/&|'
    100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0   oneFilone/e
    

I've basically tried a whole bunch of things matching for:

  • [^\t ]*$|one/&
  • [^[[:space:]]]*$|one/&

and the ones listed above. The closest I can get is to have oneFilone/e, which was [^[[:blank:]]]*$|one/&|', or to pre-pend to the 0, but I can't seem to quite get what I want.

EDIT Because a few people have commented / posted answers, none of which work for me, I figured I'd add: I am using Mac OS X 10.7.3. The version of sed I'm not completely sure of (if anybody knows a way to get it feel free to add a comment to that effect) - the man sed page says it's a BSD sed. I'm not sure how different that is to GNU sed, if any.

I'm also using zsh, with oh-my-zsh running (prettymuch unmodified). I have turned on extended_glob (setopt extended_glob).

I've commented with my results for the answers people have given; I assume they are run on a Linux distribution? I don't have access to a non-OS X system tonight, but I will re-run any answers tomorrow; maybe it's just my [shell|OS|bad karma] that isn't letting them work for me.

EDIT Again:

So I've tested on a Ubuntu system, and the (1) above does work. I'd love a working version for my Mac, though.

Final EDIT: Thanks to all who answered with working equivalent commands. It turns out that my first one does work, but not with BSD sed. I do, however, have gsed available (thanks for pointing that out!) which makes these all magically work.

simont
  • 68,704
  • 18
  • 117
  • 136
  • 1
    hmm, your first option seems to work for me (I made a new git repository, committed a file, and used the exact command in your option 1). You might also try `sed -r 's|([^\t]+)$|one/\1|'` which uses explicit backreferences (-r switch to use "normal" regex). – mathematical.coffee Mar 01 '12 at 23:36
  • Worked for me too on msysgit, actually 1 and 2 worked for me. – macduff Mar 01 '12 at 23:37
  • @mathematical.coffee `sed -r` gives `sed: illegal option -- r`. I'm attempting to work out what version of `sed` I'm using - best guess based on the man page is "BSD sed" (I'm using Mac OS X). – simont Mar 01 '12 at 23:44
  • Oh, Mac OS X has a different version of sed (I'm using the GNU one). From memory, you can use `-E`. (Or, `ssed` with `-r`). – mathematical.coffee Mar 01 '12 at 23:47
  • 1
    @mathematical.coffee Awesome - with `-E`: `$ git ls-files -s | sed -E 's|([^\t]+)$|one/\1|'` which gives me `one/100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0 oneFile`. – simont Mar 01 '12 at 23:52
  • @macduff Maybe it's just something I'm running which isn't playing nice - `zsh`, `osx`, my version of `sed` or just bad karma. I did think I had the right regex, though, so I'm glad it works for somebody. – simont Mar 01 '12 at 23:53
  • From the output you've been getting, it looks like your shell is not interpreting that character as a tab `\t`, but as something else. Perhaps your shell converts `\t` to 4 spaces? – mathematical.coffee Mar 02 '12 at 00:01
  • @mathematical.coffee The second example in the question should cover that - I matched for `[^\t ]`, which got me prepending to the `0` - only one block-of-whitespace off. – simont Mar 02 '12 at 00:03

5 Answers5

1

The following works for me:

git ls-files -s | sed -e 's|\t\(.\+\)$|\tone/\1|g'

100644 345242cb0c4e9bb01a6fef9947f4342ff2f68553 0       one/ExView/resource.h

would have been

100644 345242cb0c4e9bb01a6fef9947f4342ff2f68553 0       ExView/resource.h

I've also had trouble when I got strange new line problems. I doubt this is your problem, but in the past, git ls-files -s | tr -d '\r' | ... has been helpful for me.

macduff
  • 4,655
  • 18
  • 29
  • You've understood perfectly :) Except that isn't working for me: `git ls-files -s | sed -e 's|\t\(.\+\)$|\tone/\1|g'` gives `100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0 oneFile`. – simont Mar 01 '12 at 23:46
  • I know this is crazy, but what about escaping the `/`, like `\/` – macduff Mar 01 '12 at 23:55
  • `$ git ls-files -s | sed -e 's|\t\(.\+\)$|\tone\/\1|g'` - no such luck, I'm afraid. I don't get it appended anywhere. – simont Mar 01 '12 at 23:57
1

This is simpler, but seems to work:

echo -e '100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0 oneFile' | sed -e 's/\([a-zA-Z]\+\)$/one\/\0/g'
100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0 one/oneFile

It should work with a tab just before oneFile also.

Eduardo Ivanec
  • 11,668
  • 2
  • 39
  • 42
  • $ git ls-files -s | sed -e 's/\([a-zA-Z]\+\)$/one\/\0/g' 100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0 oneFile – simont Mar 01 '12 at 23:46
  • That's not the regex I posted! `git ls-files -s | sed -e 's/\([a-zA-Z]\+\)$/one\/\0/g' 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one/oneFile` – Eduardo Ivanec Mar 02 '12 at 01:25
  • Oh, I do apologize, my mistake. I don't get it working for the one you *did* post, though. Yours works on the Ubuntu box I got onto - just not on Mac OS X. I think my `sed` version messes things up. – simont Mar 02 '12 at 01:32
  • Ah! That explains it : ) Do you have 'gsed' available? – Eduardo Ivanec Mar 02 '12 at 03:11
1

unable to test right now, but you're using too many brackets with the character classes.

[^[[:space:]]]*$|one/&

should be

[^[:space:]]*$|one/&

With the extra brackets, you get just the characters '[',':','s','p','a','c','e',']' -- explaining why the dir is inserted before the last 'e'

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

This might work for you (or am I missing something?):

echo -e "100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0\toneFile" | sed 's/\t/&one\//'
100644 bfadfab6f98b8fa1e9989fe16b2bf0fb13ffd39e 0       one/oneFile
potong
  • 55,640
  • 6
  • 51
  • 83
0

I wanted to do exactly this but did not have gsed around...

The only issue is that OS X sed does not recognise \t as a TAB character, as explained here. You have to use an actual TAB character. Use Option 1 in the original post, but instead of typing \t, press Ctrl+v followed by the Tab key. This is hard to copy and paste here, so you will have to do it yourself. :-)

See also this question.

Community
  • 1
  • 1
llude
  • 1,173
  • 8
  • 10