I have a Lenovo laptop connected to a Lenovo docking station. A USB headset is plugged into the docking station. Occasionally, after a reboot, the headset's PCM device switches places with the docking station's sound card. As a result, sysctl hw.snd.default_unit=x no longer points to the headset, and I lose audio output.
Is there a way to ensure that the USB headset always remains the default sound device, regardless of the boot order?
I have a Lenovo laptop connected to a Lenovo docking station. A USB headset is plugged into the docking station. Occasionally, after a reboot, the headset's PCM device switches places with the docking station's sound card. As a result, sysctl hw.snd.default_unit=x no longer points to the headset, and I lose audio output.
Is there a way to ensure that the USB headset always remains the default sound device, regardless of the boot order?
You may be able to leverage devd to match on the desired device and sysctl to set it to the default.
Heres a sample that I use to set permissions on a Nikon D610 when it's plugged in. It lives in /usr/local/etc/devd/d610.conf. Change the vendor and product to your USB device, action would be calling the sysctl. Not sure how the "x" would map to $cdev or other variable available to devd.
notify 100 {
match "vendor" "0x04B0";
match "product" "0x0434";
action "chgrp wheel /dev/$cdev && chmod 660 /dev/$cdev";
};
Thanks to both of you for the helpful tips!
In the end, I created this script (with a little help from AI):
Code:
$ cat /root/default_sound_scripter.sh
#!/bin/sh
# Extract the pcm number for Plantronics
plantronics_pcm=$(cat /dev/sndstat | grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//')
# If found, set it as default
if [ -n "$plantronics_pcm" ]; then
sysctl hw.snd.default_unit=$plantronics_pcm
else
echo "Plantronics device not found in /dev/sndstat"
fi
And it's triggered by this devd rule:
Code:
$ cat /usr/local/etc/devd/plantronics.conf
attach 100 {
match "vendor" "0x047f"; # Replace with actual vendor ID
match "product" "0xc056"; # Replace with actual product ID
action "/root/default_sound_scripter.sh";
};
Thanks to both of you for the helpful tips!
In the end, I created this script (with a little help from AI):
Code:
$ cat /root/default_sound_scripter.sh
#!/bin/sh
# Extract the pcm number for Plantronics
plantronics_pcm=$(cat /dev/sndstat | grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//')
# If found, set it as default
if [ -n "$plantronics_pcm" ]; then
sysctl hw.snd.default_unit=$plantronics_pcm
else
echo "Plantronics device not found in /dev/sndstat"
fi
And it's triggered by this devd rule:
Code:
$ cat /usr/local/etc/devd/plantronics.conf
attach 100 {
match "vendor" "0x047f"; # Replace with actual vendor ID
match "product" "0xc056"; # Replace with actual product ID
action "/root/default_sound_scripter.sh";
};
Awesome! That's a good solution. You can create the action script and test independently, create the devd rule test independently, then when they both work, hook them together. Thanks for the update.
Thanks to both of you for the helpful tips!
In the end, I created this script (with a little help from AI):
Code:
$ cat /root/default_sound_scripter.sh
#!/bin/sh
# Extract the pcm number for Plantronics
plantronics_pcm=$(cat /dev/sndstat | grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//')
# If found, set it as default
if [ -n "$plantronics_pcm" ]; then
sysctl hw.snd.default_unit=$plantronics_pcm
else
echo "Plantronics device not found in /dev/sndstat"
fi
And it's triggered by this devd rule:
Code:
$ cat /usr/local/etc/devd/plantronics.conf
attach 100 {
match "vendor" "0x047f"; # Replace with actual vendor ID
match "product" "0xc056"; # Replace with actual product ID
action "/root/default_sound_scripter.sh";
};
I'm very glad that you found solution, and that your script works, just one question: did AI recommended using awk? In my experience with AI (which is not huge, TBH) it tends to bring out heavy artillery even when there is no actual need for that.
As much as I love awk(1), instead of | awk -F: '{print $1}' | , you can get same result with simpler | cut -d: -f1 |
If you wanted awk, it could be done with awk only, without cat, grep, sed and pipes, something like: $(awk '/Plantronics/ { split($0, a, ":"); gsub("pcm", "", a[1]); print a[1] }' /dev/sndstat)
If you wanted awk, it could be done with awk only, without cat, grep, sed and pipes, something like: $(awk '/Plantronics/ { split($0, a, ":"); gsub("pcm", "", a[1]); print a[1] }' /dev/sndstat)
Variations on a theme. For the awk minded: awk -Fpcm '/Plantronics/ { print substr($2,1,1) }'
If, however, one has a lot of pcm devices, because, well, one does , then a variation on covacat's solution: sed -nE '/Plantronics/ s/pcm([^:]*):.*/\1/ p' *
In my view the most important easy win, compared to the three step solution, is to realise that you can use a regular expression as an address for the line you are interested in; that holds for both sed(1) and awk(1). This may also be the easiest to remember when you need to solve similar problems.
___
* this resembles grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//' in how things are matched.
Edit: slightly changed the layout of the commands.
Variations on a theme. For the awk minded: awk -Fpcm '/Plantronics/{print substr($2,1,1)}'
If, however, one has a lot of pcm devices, because, well, one does , then a variation on covacat's solution: sed -nE '/Plantronics/s/pcm([^:]*):.*/\1/p' *
In my view the most important easy win, compared to the three step solution, is to realise that you can use a regular expression as an address for the line you are interested in; that holds for both sed(1) and awk(1). This may also be the easiest to remember when you need to solve similar problems.
___
* this resembles grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//' in how things are matched.
Thanks! Those are great examples, and I really like and appreciate them as I'm of opinion that regex should be first (okay, maybe second) thing to learn for anyone interested in Unix or Linux, but there is another more important thing – KISS principle.
For example, I have bunch of scripts that I wrote some years ago that are heavily based on sed(1); I had great time writing them and I was very proud of myself for coming up with right syntax, but if I look at them now, I'm like: "WTF is this line doing, and what I was thinking when I wrote this???"
Using simpler cat | grep | cut | tr | (simple) sed or something similar, will allow for the scripts to stay readable and understandable for us, even when we age.
In all seriousness, there is a balance to be struck.
There is a big difference between thinking in regular expressions—combined or not with exploiting sed's limited capabilities of a state—and reading, even decrypting, regular expressions after they have been designed and written. That's the price that has to be paid for the brevity and power of regular expressions covering this class of languages. Regexes are part of so many languages and useful in so many domains, that one can hardly afford not to learn their basic use.
or any equivalent sources. I've found these of indispensable value. It's not even required to learn it by heart: not as in Everything Everywhere All at Once, learn as you go and practice!
As to the last two items, sh(1) programming beyond the basics is, in my personal view, only required when in need of portability or when contributing to FreeBSD. Personally, I don't like the lack of a basic data structure like the list or array.
If you can forego the use of libraries, awk can be a very versatile tool; IMO, for use on the command line there's hardly any competition when it comes to a balance between being non-cryptic and compact. However, as Brian Kernighan says in The AWK Programming Language, Second Edition: "Realistically, if you’re going to learn only one language, Python is the one". I view this book as a very valuable option for sysadmins and programmers. It also contains balanced considerations on when to look for other programming languages as options to solve your problems.
Returning to practical matters: in my view, the use of awk -F: '/Plantronics/ { print $1 }' /dev/sndstat | sed 's/pcm//' *
instead of: cat /dev/sndstat | grep 'Plantronics' | awk -F: '{print $1}' | sed 's/pcm//'
should be within every FreeBSD user's grasp.
However, using a regexp explicitly shouldn't be that dificult: awk -F: '/Plantronics/ { match($1, /[[:digit:]]*$/); print substr($1, RSTART, RLENGTH) }' /dev/sndstat
As for: awk -Fpcm '/Plantronics/ { print substr($2,1,1) }' sed -nE '/Plantronics/ s/pcm([^:]*):.*/\1/ p'
a general idea of what is going on here is a start. Understanding this by means of the above references requires some study and practice.
And, as you can see, cut didn't make the cut
___
* In my book, this is simple sed, not basic sed.
Excellent post Erichans, thanks! I do agree with (almost) all of what you said here, and I admit – I’m so hooked on bash arrays even when there is no need, I go for them just for fun of it
I only don’t understand what you have against cut? I can’t imagine writing any of my monstrosities of one-liners without it. For example, some time ago, I was looking at HECnet node name database information service at MIM (MIM is currently not responsive, that’s why archive) and I thought "Can I figure out how many people are running those nodes, sort them by surname and pretty print so that all can fit on one screen?"
BTW, here is example of sed using address for the lines
Disclaimer and trigger warning: this is ugly; kids, don’t be like me!
1. Mark Abene 2. Peter Allan 3. Anders Andersson 4. Brian Angus 5. Paul Anokhin 6. Robert Armstrong
7. Madeline Autumn-Rose 8. Moishe Bar 9. Mark Benson 10. Jean-Yves Bernier 11. Mark Berryman 12. Johnny Billquist
13. Mark J. Blair 14. Robin Blair 15. Tony Blews 16. Wilm Boerhout 17. Dennis Boone 18. Jason Brady
19. Olof Breuer 20. Jeroen Brons 21. David Brown 22. Christine Caulfield 23. Jesper Christensen 24. Dana Chus
25. Fred Coffey 26. Peter Coghlan 27. Dave Comley 28. Mark Curtis 29. Mark Darvill 30. Steve Davidson
31. Thomas DeBellis 32. Johan Dees 33. Thierry Dusset 34. Pete Edwards 35. Joe Ferraro 36. James Gay
37. Lee Gleason 38. Andy Green 39. Ed Groenenberg 40. Chuck Guldenschuh 41. Keith Halewood 42. Douglas Hall
43. Michael Hamer 44. Kurt Hamm 45. Iain Hardcastle 46. Zane Healy 47. Brian Hechinger 48. Michael Holmes
49. Ulli Hölscher 50. Vladimir Isakov 51. Peter Jackson 52. Yvan Janssens 53. Rob Jarratt 54. Jeffrey Johnson
55. John Kemker 56. Paul Koning 57. Fedor Konstantinov 58. Mike Kostersitz 59. Sampsa Laine 60. Ruslan Laishev
61. Peter Löthberg 62. Karl Maftoum 63. Stuart Martin 64. Mark Matlock 65. Dave McGuire 66. Ian McLaughlin
67. Gordon Miller 68. Eric Moore 69. David Moylan 70. Tony Nicholson 71. Thord Nilsson 72. Bruno Novak
73. Robert Nydahl 74. Erik Olofsen 75. Ales Petan 76. Jordi Guillaumes Pons 77. Tomas Prybil 78. John H. Reinhardt
79. Christopher Rivett 80. Katherine Rohl 81. Lex van Roon 82. Brian Roth 83. Oleg Safiullin 84. Supratim Sanyal
85. Fausto Saporito 86. Saku Setala 87. Tim Sneddon 88. Michael Spencer 89. Timothy Stark 90. Jason Stevens
91. Mark Thomas 92. August Treubig 93. Unknown 94. Oscar Vermeulen 95. Björn Victor 96. Rok Vidmar
97. Hans Vlems 98. Reindert Voorhorst 99. Trevor Warwick 100. Gullik Webjörn 101. Peter Whisker 102. Mark Wickens
103. Dan Williams 104. John Wilson 105. Julian Wolfe 106. Frank Wortner 107. Alice Wyan 108. John Yaldwyn
109. Connor Youngquist
Well, you asked ...
Thank you for providing an example, solution and example run; I found the explanation helpful too. Unfortunately, often a problem or code trying to solve a problem comes without a useful description.
As to my previous answer, I had a longer answer prepared but I trimmed it. There is of course nothing against cut(1) or tr(1). In my personal, limited, experience when I want to go from a to b, sometimes I need more than cut(1) can provide, or I can easily integrate the desired functionality in adjacent commands at the other side of the fence, that is: |. That might be considered a personal preference. All utilities have their particular use cases. However, when one chooses to use more utilities (as opposed to some extra coding), it usually pays to carefully read man pages to avoid unnecessary coding. nl(1) provides useful options in this case.
Regarding the problem at hand. I imagine the original source is better formatted (e.g. XML or HTML). In that case, a parser would be a good tool. Often, we have to work with the cards we're dealt. If further, more flexible data manipulation is needed, the obvious approach would be to structure all the data and store it appropriately—perhaps in a database. As we're dealing with plain text (not even <tab> formatted), sed & awk (with additional assistance) seem like good candidates. As a result of working on my sed solutions, I can't see an awk solution that does things differently or more easily than sed, so I only used sed.
Two things that sed cannot do is sorting and formatting columns, so external help is needed here. In addition to sed(1), only one call of sort(1) and column(1) remain as part of my second solution. You might have already noticed by careful reading: I've cut out cut(1), again! However, I agree that using cut is appropriate in this context, even though it increases the number of commands and pipes.
Where I don't comment my first solution, I do provide comments in the script files of my second solution. There, I also handled the right justification of numbers below 100. In much the same way you did; however, one doesn't have to do that when using nl(1), as is demonstrated in the first solution.
I've decided to put both solutions in a spoiler. If one wants to work out an alternative solution without my solutions in plain view, one can choose to do so; viewing them is just a couple of clicks away though. While using sed, I ran into what looks like a sed bug. That took considerable time to analyse; I've worked around it. I used the file i, created by: elinks -dump "https://web.archive.org/web/20240316090019/http://mim.stupi.net/nodedb" > i
Main processing steps - second solution
Rich (BB code):
$ cat i | \
sed -Ef preSort.sed | \
sort -uk1 | \
sed -Ef postSortLN.sed | \
sed -Ef mergeRjustLN.sed | \
column -x
Rich (BB code):
$ cat i | \
sed -n '37,1143 p' | \
cut -c 20-45 | \
sed -E 's/ *$//;s/(.* )([^ ]+)$/\2\1/' | \
sort -uk1 | \
sed -E 's/^([^ ]+)( .*)/\2\1/' | \
nl -nrn -s'.' | \
column -x
Rich (BB code):
[1-0] % cat preSort.sed
# select table rows by deleting other lines
# cut out name column at the start of first name
# remove line ending spaces
# move surname to the front as preparation for sort
1,36 d
1144,$ d
s/.{20}(.{22}).*/\1/
s/ *$//
s/(.*) ([^ ]+)$/\2 \1/
[2-0] % cat postSortLN.sed
# restore original name order
# generate line numbers, to be processed later
s/^([^ ]+) (.*)/\2 \1/
=
[3-0] % cat mergeRjustLN.sed
# merge line numbers
# add line number formatting
N
s/\n/. /
# right justify single and double digit line numbers
s/^[^.]\./ &/
s/^[^.]{2}\./ &/
Rich (BB code):
$ cat i | sed -Ef preSort.sed | sort -uk1 | sed -Ef postSortLN.sed | sed -Ef mergeRjustLN.sed | column -x
1. Mark Abene 2. Peter Allan 3. Anders Andersson 4. Brian Angus 5. Paul Anokhin
6. Robert Armstrong 7. Madeline Autumn-Rose 8. Moishe Bar 9. Mark Benson 10. Jean-Yves Bernier
11. Mark Berryman 12. Johnny Billquist 13. Mark J. Blair 14. Robin Blair 15. Tony Blews
<snap>
101. Peter Whisker 102. Mark Wickens 103. Dan Williams 104. John Wilson 105. Julian Wolfe
106. Frank Wortner 107. Alice Wyan 108. John Yaldwyn 109. Connor Youngquist
___
Edit: there's final editing for you: spaces fly where there not supposed to. Changed preSort.sed as intended.
Excellent! Thanks Only two problems that I see:
1) First and Last name are merged into one string "LastFirst"; it's not "Autumn-RoseMadeline" but "Madeline Autumn-Rose", "J. BlairMark" vs "Mark J. Blair" etc. In my version, Name comes first, then Middle Name initial, then Surname, but all are sorted by surname.
and
2) It's not one-liner and do I love my monstrosities of one-liners very much.
Perhaps someone who is good with Perl (unfortunately, I'm not) can do it as one line solution?
BTW, I just spotted an error in version that I posted, I had better one which was trowing out all Unknown (like 93. Unknown). I was interested in known persons list, not number of nodes.
P.S. Edit: Thanks for the edit, Erichans; now output looks much better, but still, it's not one-liner
That's what I'm talking about, kudos Erichans Much shorter, much smarter and far more elegant than my version.
Also, it works as true one liner with elinks -dump, no need for cat i.
Again, thanks and congrats
P.S. Edit:
Apologies to OP zsolt for turning this thread, which was about sound devices order, into regex exercise in real /. style
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.