I extracted the metadata of a PDF in a .txt file using pdftk, and now I am trying to decrease the BookmarkPageNumber value for each bookmark by an integer. The .txt has these lines:

BookmarkTitle: Preface
BookmarkLevel: 1
BookmarkPageNumber: 10
BookmarkTitle: Author
... and so on

I am trying to do this using sed's substitute command, and here is what I have so far:

// $1 is the source .txt file; $2 is the decrement
// __ is a placeholder for the variable with the original value
cat $1 | sed "s/BookmarkPageNumber: [0-9]*/BookmarkPageNumber: `expr __ - $2`/" | cat > metadata.txt

How can I put the original value in a variable, and then replace the palceholder __ with it, within this same sed expression?


For that purpose is better use awk so as it support arithmetic operations

cat $1 | awk -v d=$2 '/BookmarkPageNumber:/{$2-=d}1'
  • Oh man, this worked perfectly! Thank you very much! – netcentric Oct 26 '14 at 18:23
  • By the way, what does the 1 at the end of the expression mean? – netcentric Oct 26 '14 at 18:28
  • 3
    @netcentric According to awk grammar: An AWK program is a sequence of pattern {action} pairs. One, but not both, of pattern {action} can be omitted. If {action} is omitted it is implicitly {print}. If pattern is omitted, then it is implicitly matched. So it just the trick to shortage your function: use the pattern 1 which accept every line and can ommit {print} – Costas Oct 26 '14 at 18:45
awk '!/BookmarkPageNumber:/ {print}; /BookmarkPageNumber:/ {print $1 " " $2-1}' old.txt > new.txt

Another choice would be to use the shell itself (using 5 as an example, change it to whatever the actual integer you want to substract is):

while read key val; do 
    [[ $key == "BookmarkPageNumber:" ]] && let val=val-5;
    echo $key $val; 
done < file 

Or, you could use a tool like perl:

perl -pe 's/(BookmarkPageNumber:\s*)(\d+)$/$1 . ($2-5)/e; ' file 

And here's another awk approach:

awk '$1~/BookmarkPageNumber:/{$2=$2-5}1;' file 
  • your scripts are nice but you are forget the task: change just BookmarkPageNumber, not all digital fields. – Costas Oct 26 '14 at 18:08
  • HINT to check variable is digit compare it with oneself plus null [ $var = $[$var+0] ] – Costas Oct 26 '14 at 18:12
  • @Costas argh, indeed I was, thanks. In that case, most of my caveats were not needed anyway since this should be very standardized data. Also thanks for the hint, that's clever but will throw an error when it fails, so I'd rather use [0-9]. – terdon Oct 26 '14 at 18:20
  • in last awk is overcontrol: is enough find the line with pattern not first field. Secondary OP ask for different decrement not just 1. Hint make an error just in case number with last alpha but, of course, your are free to choice. – Costas Oct 26 '14 at 18:35
  • @Costas there is no reason to assume that the string can't exist elsewhere in the line so restricting the match to the 1st field seems reasonable. Good point about not being 1, I changed the format so that any number can be used. I have no idea what your last suggestion means. What alpha? – terdon Oct 26 '14 at 18:40

NOTE: I just realised that he wanted to decrease the BookmarkPageNumber by an arbitrary integer, which I initially overread, and my solution below only works for decreasing it by 1. However, I feel hard pressed removing entirely considering the effort I put into it:

--------partial solution only---------

In case you are hell-bent to only use sed, I have this 1 liner for you: sed -r '/^BookmarkPageNumber: /{s/([0-9]*)$/\1@/;:loop {s/0@/@9/;/0@/b loop;};s/1@/_0/;s/2@/_1/;s/3@/_2/;s/4@/_3/;s/5@/_4/;s/6@/_5/;s/7@/_6/;s/8@/_7/;s/9@/_8/;s/ @9+$/ UNDERFLOW/;s/ _0*/ /;s/_//}' $1 >metadata.txt

Note: this only works for natural decimal numbers, I hope this is okay for you.

Oh, it even has underflow detection. So in case you want your page numbers to saturate down to 0, just replace UNDERFLOW by 0

And I agree with anyone who calls this a mere intellectual wankery, because that's what it is.

p.s.: You should probably check that your input and output file descriptors don't point to the same file, or else the file will get truncated if the user is having write access.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.