Danh sách chỉ thư mục sử dụng ls trong Bash?


954

Lệnh này liệt kê các thư mục trong đường dẫn hiện tại: ls -d */

Chính xác thì mô hình */làm gì?

Và làm thế nào chúng ta có thể đưa ra đường dẫn tuyệt đối trong lệnh trên (ví dụ ls -d /home/alice/Documents) để chỉ liệt kê các thư mục trong đường dẫn đó?


7
Xem chỉnh sửa cho câu trả lời này unix.stackexchange.com/a/75047/32647 giải thích ý -dnghĩa thực sự của nó.
Evgeni Sergeev

Câu trả lời:


992

*/là một mẫu phù hợp với tất cả các thư mục con trong thư mục hiện tại ( *sẽ khớp với tất cả các tệp thư mục con; /giới hạn nó trong các thư mục). Tương tự, để liệt kê tất cả các thư mục con bên dưới / home / alice / Documents, hãy sử dụngls -d /home/alice/Documents/*/


88
Lưu ý rằng */sẽ không khớp với bất kỳ thư mục ẩn nào. Để bao gồm chúng, hoặc chỉ định chúng rõ ràng như thế ls -d .*/ */, hoặc đặt dotglobtùy chọn trong Bash.
Gergő

1
@ytpillai Điều đó xảy ra vì không có thư mục con nào được liệt kê. Bạn nhận được lỗi đó bất cứ lúc nào bạn sử dụng lstrên một cái gì đó không tồn tại.
Gordon Davisson

1
Được rồi, cảm ơn, tôi đã kiểm tra điều đó ngay bây giờ và hóa ra thư mục duy nhất là thư mục gốc.
ytpillai

1
@GordonDavisson Có thể tránh được thông báo về sự thất bại với tùy chọn bashshopt -s nullglob

4
@BinaryZebra Cài đặt nullglob sẽ tránh thông báo lỗi, nhưng sẽ gây ra nhiều nhầm lẫn hơn: với tập hợp nullglob và không có thư mục con, ls */sẽ mở rộng sang chỉ lsliệt kê tất cả các tệp trong thư mục hiện tại. Tôi khá chắc chắn rằng đó không phải là điều bạn muốn trong trường hợp đó.
Gordon Davisson

481

Bốn cách để thực hiện điều này, mỗi cách có một định dạng đầu ra khác nhau

1. Sử dụng echo

Ví dụ : echo */, echo */*/
Đây là những gì tôi nhận được:

cs/ draft/ files/ hacks/ masters/ static/  
cs/code/ files/images/ static/images/ static/stylesheets/  

2. lsChỉ sử dụng

Ví dụ: ls -d */
Đây là chính xác những gì tôi nhận được:

cs/     files/      masters/  
draft/  hacks/      static/  

Hoặc như danh sách (có thông tin chi tiết): ls -dl */

3. Sử dụng lsgrep

Ví dụ: ls -l | grep "^d" Đây là những gì tôi nhận được:

drwxr-xr-x  24 h  staff     816 Jun  8 10:55 cs  
drwxr-xr-x   6 h  staff     204 Jun  8 10:55 draft  
drwxr-xr-x   9 h  staff     306 Jun  8 10:55 files  
drwxr-xr-x   2 h  staff      68 Jun  9 13:19 hacks  
drwxr-xr-x   6 h  staff     204 Jun  8 10:55 masters  
drwxr-xr-x   4 h  staff     136 Jun  8 10:55 static  

4. Bash Script (Không được đề xuất cho tên tệp chứa khoảng trắng)

Ví dụ: for i in $(ls -d */); do echo ${i%%/}; done
Đây là những gì tôi nhận được:

cs  
draft  
files  
hacks  
masters  
static

Nếu bạn muốn có '/' là ký tự kết thúc, lệnh sẽ là: for i in $(ls -d */); do echo ${i}; done

cs/  
draft/  
files/  
hacks/  
masters/  
static/

2
Lưu ý 3chậm hơn đáng kể so với những người khác đối với tôi. Trong thư mục tôi đã kiểm tra: 1.012s, 2.016s, 3 .055s , 4.021s.
đẳng cấu

@isomorphismes, bạn đã đo thời gian như thế nào? Bằng phương tiện gì? Nhân tiện, tôi phải nói rằng, số 1là sở thích của tôi.
Albert

Albert, tôi đã sử dụng time, chức năng Unix. /// Tôi đồng ý, echo */là thông minh hơn ls -d */.
đẳng cấu

9
Hai điều nhỏ mà bạn cần lưu ý. ls -l | grep "^ d" lọc ra các liên kết tượng trưng đến các thư mục, trong khi 'ls -d * /' hiển thị chúng. Ngoài ra, 'ls -d * /' không thành công nếu không có thư mục nào hiện diện trong thư mục đích.
Zoran Pavlovic

15
Các 1.tùy chọn sẽ gây ra đau lớn khi giao dịch với tên thư mục mà có các khoảng trống.
Shrikant Sharat

99

Tôi sử dụng:

ls -d */ | cut -f1 -d'/'

Điều này tạo ra một cột duy nhất không có dấu gạch chéo - hữu ích trong các tập lệnh.

Theo quan điểm của tôi.


18
Các -1lựa chọn cho lsđầu ra sẽ ở dạng single-cột là tốt, như trong ls -1 -d */.
Kalin

3
@ user29020 Cần lưu ý rằng -1vẫn đưa ra dấu gạch chéo cho mỗi mục nhập.
Dennis

Khi tôi thêm nó vào .bashrcnhư bí danh, làm thế nào để tôi thêm '/'?
RNA

@RNA muộn để trả lời, nhưng đối với những người trong tương lai: hãy thử trốn thoát bằng dấu gạch chéo ngược? '\/'
Caek

1
Phá vỡ nếu có một dấu gạch chéo hàng đầu. Điều này chỉ xóa các dấu gạch chéo:| sed -e 's-/$--'
Victor Sergienko

62

Đối với tất cả các thư mục không có thư mục con:

find /home/alice/Documents -maxdepth 1 -type d

Đối với tất cả các thư mục thư mục con:

find /home/alice/Documents -type d

2
Để khớp với câu hỏi (nhưng bao gồm các dotfiles), đầu tiên phải là: find /home/alice/Documents -maxdepth 1 -mindepth 1 -type dnếu không bạn bao gồm /home/alice/Documentschính nó. Để bao gồm các liên kết tượng trưng đến thư mục, tiền tố với-L
jhnc

Điều này có vẻ giống như các giải pháp sanest, vì find là công cụ thực sự được thực hiện cho nhiệm vụ này. Bạn có thể loại trừ các kết quả đầu tiên - ./ cho thư mục hiện hành - bằng đường ống để grep -v '^ \ ./$'
siliconrockstar

38

4 (thêm) Tùy chọn đáng tin cậy.

Một dấu hoa thị không được trích dẫn* sẽ được hiểu là một mẫu (toàn cầu) bởi vỏ.
Shell sẽ sử dụng nó trong việc mở rộng tên đường dẫn.
Sau đó, nó sẽ tạo ra một danh sách tên tệp phù hợp với mẫu.
Một dấu hoa thị đơn giản sẽ khớp với tất cả tên tệp trong PWD (thư mục làm việc hiện tại).
Một mô hình phức tạp hơn */sẽ phù hợp với tất cả các tên tệp kết thúc /.
Như vậy, tất cả các thư mục. Đó là lý do tại sao lệnh:

1.- tiếng vang.

echo */
echo ./*/              ### avoid misinterpreting filenames like "-e dir"

sẽ được mở rộng (bằng vỏ) cho echotất cả các thư mục trong PWD.


Để kiểm tra điều này: Tạo một thư mục ( mkdir) có tên như test-dir và cdvào đó:

mkdir test-dir; cd test-dir

Tạo một số thư mục:

mkdir {cs,files,masters,draft,static}   # safe directories.
mkdir {*,-,--,-v\ var,-h,-n,dir\ with\ spaces}  # some a bit less secure.
touch -- 'file with spaces' '-a' '-l' 'filename'    # and some files:

Lệnh echo ./*/sẽ vẫn đáng tin cậy ngay cả với các tệp có tên kỳ lạ:

./--/ ./-/ ./*/ ./cs/ ./dir with spaces/ ./draft/ ./files/ ./-h/
./masters/ ./-n/ ./static/ ./-v var/

Nhưng không gian trong tên tệp làm cho việc đọc hơi khó hiểu.


Nếu thay vì echo, chúng tôi sử dụng ls, shell vẫn là thứ đang mở rộng danh sách tên tệp. Shell là lý do để có được một danh sách các thư mục trong PWD. Các -dtùy chọn để lslàm cho nó liệt kê các mục nhập thư mục hiện tại thay vì nội dung của mỗi thư mục (như đã trình bày theo mặc định).

ls -d */

Tuy nhiên, lệnh này là (phần nào) ít đáng tin cậy. Nó sẽ thất bại với các tập tin có tên kỳ lạ được liệt kê ở trên. Nó sẽ nghẹt thở với một vài cái tên. Bạn cần phải xóa từng cái một cho đến khi bạn tìm thấy những vấn đề.

2.-

GNU lssẽ chấp nhận phím "kết thúc tùy chọn" ( --).

ls -d ./*/                     ### more reliable BSD ls
ls -d -- */                    ### more reliable GNU ls

3.-printf

Để liệt kê mỗi thư mục trong dòng riêng của nó (trong một cột, tương tự như ls -1), hãy sử dụng:

$ printf "%s\n" */        ### Correct even with "-", spaces or newlines.

Và, thậm chí tốt hơn, chúng ta có thể xóa dấu vết /:

$ set -- */; printf "%s\n" "${@%/}"        ### Correct with spaces and newlines.

Một nỗ lực như thế này:

$ for i in $(ls -d */); do echo ${i%%/}; done

Sẽ thất bại về:

  • một số tên ( ls -d */) như đã được hiển thị ở trên.
  • sẽ bị ảnh hưởng bởi giá trị của IFS.
  • sẽ phân chia tên trên dấu cách và tab (với mặc định IFS).
  • mỗi dòng mới trong tên sẽ bắt đầu một lệnh echo mới.

4.- Chức năng

Cuối cùng, sử dụng danh sách đối số bên trong một hàm sẽ không ảnh hưởng đến danh sách đối số của trình bao hiện tại. Đơn giản:

$ listdirs(){ set -- */; printf "%s\n" "${@%/}"; }
$ listdirs

trình bày danh sách này:

--
-
*
cs
dir with spaces
draft
files
-h
masters
-n
static
-v var

Tùy chọn này an toàn với một số loại tên tệp lẻ.


Tôi nghĩ bạn nên thay thế từ An toàn bằng Đáng tin cậy. Bài đăng của bạn khiến tôi nghĩ rằng các giải pháp khác là 'không an toàn' (dễ bị tổn thương hoặc có thể khai thác.)
Amado Martinez

1
@AmadoMartinez Đã hoàn thành một s chung / an toàn / đáng tin cậy /. Tốt hơn?

21

Các treelệnh cũng khá hữu ích ở đây. Theo mặc định, nó sẽ hiển thị tất cả các tệp và thư mục ở độ sâu hoàn chỉnh, với một số ký tự ascii hiển thị cây thư mục.

$ tree
.
├── config.dat
├── data
   ├── data1.bin
   ├── data2.inf
   └── sql
|      └── data3.sql
├── images
   ├── background.jpg
   ├── icon.gif
   └── logo.jpg
├── program.exe
└── readme.txt

Nhưng nếu chúng ta chỉ muốn lấy các thư mục, không có cây ascii và với đường dẫn đầy đủ từ thư mục hiện tại, bạn có thể làm:

$ tree -dfi
.
./data
./data/sql
./images

Các đối số là:

-d     List directories only.
-f     Prints the full path prefix for each file.
-i     Makes tree not print the indentation lines, useful when used in conjunction with the -f option.

Và nếu sau đó bạn muốn đường dẫn tuyệt đối, bạn có thể bắt đầu bằng cách chỉ định đường dẫn đầy đủ đến thư mục hiện tại:

$ tree -dfi "$(pwd)"
/home/alice/Documents
/home/alice/Documents/data
/home/alice/Documents/data/sql
/home/alice/Documents/images

Và để giới hạn số lượng thư mục con, bạn có thể đặt mức tối đa của thư mục con với -L level, ví dụ:

$ tree -dfi -L 1 "$(pwd)"
/home/alice/Documents
/home/alice/Documents/data
/home/alice/Documents/images

Nhiều tranh luận có thể được nhìn thấy với cây người


15

Trong trường hợp bạn đang tự hỏi tại sao đầu ra từ 'ls -d * /' cung cấp cho bạn hai dấu gạch chéo, như:

[prompt]$ ls -d */    
app//  cgi-bin//  lib//        pub//

có thể là do ở đâu đó các tệp cấu hình shell hoặc session của bạn đặt bí danh lệnh ls thành phiên bản ls có chứa cờ -F. Cờ đó gắn thêm một ký tự cho mỗi tên đầu ra (đó không phải là tệp đơn giản) cho biết loại đó là gì. Vì vậy, một dấu gạch chéo là từ khớp với mẫu '* /' và dấu gạch chéo khác là chỉ báo loại được nối thêm.

Để thoát khỏi vấn đề này, tất nhiên bạn có thể định nghĩa một bí danh khác cho ls. Tuy nhiên, để tạm thời không gọi bí danh, bạn có thể thêm lệnh bằng dấu gạch chéo ngược:

\ ls -d * /


11

Tôi chỉ cần thêm nó vào .bashrctập tin của mình (bạn cũng có thể chỉ cần gõ nó vào dòng lệnh nếu bạn chỉ cần / muốn nó cho một phiên)

alias lsd='ls -ld */'

sau đó lsd sẽ tạo ra kết quả mong muốn.


Bạn không thể sử dụng? đường dẫn ls -l | grep dr
jrobertsz66

5
"sau đó lsd sẽ tạo ra kết quả mong muốn." Tôi cười
fbafelipe

1
Theo kinh nghiệm của tôi, nó tạo ra một kết quả.
Todd

Tôi sử dụng 'lad' (liệt kê tất cả các thư mục) và tôi cũng bao gồm '. * /' Để bao gồm cả các thư mục ẩn. Tất nhiên, innuendo cũng không bị mất đối với tôi.
DryLabRebel

11

lsGiải pháp thực tế , bao gồm cả liên kết tượng trưng đến thư mục

Nhiều câu trả lời ở đây không thực sự sử dụng ls(hoặc chỉ sử dụng nó theo nghĩa tầm thường ls -d, trong khi sử dụng ký tự đại diện cho kết hợp thư mục con thực tế. Một lsgiải pháp thực sự hữu ích, vì nó cho phép sử dụng các lstùy chọn để sắp xếp thứ tự, v.v.

Không bao gồm các liên kết tượng trưng

Một giải pháp sử dụng lsđã được đưa ra, nhưng nó thực hiện một cái gì đó khác với các giải pháp khác ở chỗ nó loại trừ các liên kết tượng trưng đến các thư mục:

ls -l | grep '^d'

(có thể chuyển qua sedhoặc awkđể cô lập tên tệp)

Bao gồm các liên kết tượng trưng

Trong trường hợp (có thể phổ biến hơn) nên bao gồm các liên kết tượng trưng đến thư mục, chúng ta có thể sử dụng -ptùy chọn ls, làm cho nó thêm một ký tự gạch chéo vào tên của các thư mục (bao gồm cả các thư mục được liên kết):

ls -1p | grep '/$'

hoặc, thoát khỏi dấu gạch chéo:

ls -1p | grep '/$' | sed 's/\/$//'

Chúng tôi có thể thêm các tùy chọn lskhi cần thiết (nếu một danh sách dài được sử dụng, -1thì không còn cần thiết nữa).

lưu ý: nếu chúng ta muốn dấu gạch chéo, nhưng không muốn chúng được tô sáng bởi grep, chúng ta có thể xóa phần tô sáng một cách khéo léo bằng cách làm cho phần khớp thực tế của dòng trống:

ls -1p | grep -P '(?=/$)'

9

Nếu thư mục ẩn không cần thiết để được liệt kê, tôi cung cấp:

ls -l | grep "^d" | awk -F" " '{print $9}'  

Và nếu các thư mục ẩn là cần thiết để được liệt kê, sử dụng:

ls -Al | grep "^d" | awk -F" " '{print $9}'

HOẶC LÀ

find -maxdepth 1 -type d | awk -F"./" '{print $2}'

Nếu bạn cần biết thêm về awk, xin vui lòng xem câu trả lời
Học viên PHP

So với các câu trả lời khác, tùy chọn đầu tiên của "ls .. | grep ... | awk" có vẻ quá kịch bản.
Lars Nordin

1
Bạn đang giả sử không có thư mục nào có khoảng trắng bên trong tên của họ.
Benjamin R

7

để hiển thị danh sách thư mục mà không cần /

ls -d */|sed 's|[/]||g'

2
hoặcls -d1 */ | tr -d "/"
ccpizza

Không cần phải gọi lệnh bên ngoài:set -- */; printf "%s\n" "${@%/}"

Tôi thích giải pháp của @wholanda. Giải pháp ngắn hơn một chút của tôi là (đã thử nghiệm & hoạt động): ls -d * / | sed 's | / || g'
klor

Ngoài ra có một bí danh lsdir rất hữu ích: bí danh lsdirs = "ls -d * / | sed 's | / || g'"
klor

6

Đây là những gì tôi đang sử dụng

ls -d1 /Directory/Path/*;


Đây thực sự là một giải pháp khá tốt để hiển thị từng dòng một.
Nikolay Frick

Nó sẽ không liệt kê các thư mục. Bạn phải thêm một dấu gạch chéo:ls -d1 /Directory/Path/*/
Skippy le Grand Gourou

6

Đối với danh sách chỉ thư mục :

ls -l | grep ^d

chỉ liệt kê các tập tin :

ls -l | grep -v ^d 

hoặc bạn cũng có thể làm như: ls -ld * /


4

Kiểm tra xem các mục là một thư mục với test -d:

for i in $(ls); do test -d $i && echo $i ; done

1
Vui lòng giải thích những gì mã này làm, và làm thế nào nó liên quan đến câu hỏi.
Ken YN

1
nó chạy lệnh 'ls' sau đó lặp qua kiểm tra đầu ra nếu mỗi là một thư mục và echo-ing nếu nó .. hoạt động nhưng có nhiều cách tốt hơn
ShoeLace

3

*/ là một mẫu phù hợp với tên tệp phù hợp với các thư mục trong thư mục hiện tại.

Để chỉ liệt kê các thư mục, tôi thích chức năng này:

# long list only directories
llod () { 
  ls -l --color=always "$@" | grep --color=never '^d'
}

Đặt nó trong .bashrc của bạn.

Ví dụ sử dụng:

llod       # long listing of all directories in current directory
llod -tr   # same but in chronological order oldest first
llod -d a* # limit to directories beginning with letter 'a'
llod -d .* # limit to hidden directories

LƯU Ý: nó sẽ bị hỏng nếu bạn sử dụng -itùy chọn. Đây là một sửa chữa cho điều đó:

# long list only directories
llod () { 
  ls -l --color=always "$@" | egrep --color=never '^d|^[[:digit:]]+ d'
}

3

FYI, nếu bạn muốn in tất cả các tệp trong nhiều dòng, bạn có thể thực hiện một ls -1tệp sẽ in mỗi tệp trong một dòng riêng biệt. file1 file2 file3


3

Một danh sách đơn giản của thư mục hiện tại, nó sẽ là:

ls -1d ./*/

Bạn có muốn nó được sắp xếp và sạch sẽ?

ls -1d ./*/ | cut -c 3- | rev | cut -c 2- | rev | sort

Hãy nhớ rằng: các ký tự viết hoa có hành vi khác nhau trong sắp xếp


3

Hãy thử cái này Nó hoạt động cho tất cả các distro linux.

ls -ltr | grep drw

4
ls -l | grep '^d' | awk '{print $9}'
dw1

2

Một lớp lót để liệt kê các thư mục chỉ từ "ở đây".

Với số lượng tập tin.

for i in `ls -d */`; do g=`find ./$i -type f -print| wc -l`; echo "Directory $i contains $g files."; done

2

Tôi đã giải quyết một phần với:

cd "/path/to/pricipal/folder"

for i in $(ls -d .*/); do sudo ln -s "$PWD"/${i%%/} /home/inukaze/${i%%/}; done

ln: «/home/inukaze/./.»: can't overwrite a directory
ln: «/home/inukaze/../..»: can't overwrite a directory
ln: accesing to «/home/inukaze/.config»: too much symbolics links levels
ln: accesing to «/home/inukaze/.disruptive»: too much symbolics links levels
ln: accesing to «/home/inukaze/innovations»: too much symbolics links levels
ln: accesing to «/home/inukaze/sarl»: too much symbolics links levels
ln: accesing to «/home/inukaze/.e_old»: too much symbolics links levels
ln: accesing to «/home/inukaze/.gnome2_private»: too much symbolics links levels
ln: accesing to «/home/inukaze/.gvfs»: too much symbolics links levels
ln: accesing to «/home/inukaze/.kde»: too much symbolics links levels
ln: accesing to «/home/inukaze/.local»: too much symbolics links levels
ln: accesing to «/home/inukaze/.xVideoServiceThief»: too much symbolics links levels

Vâng, điều này làm giảm cho tôi, phần thị trưởng :)


2

Thêm vào để làm cho nó đầy đủ vòng tròn, để lấy đường dẫn của mọi thư mục, sử dụng kết hợp câu trả lời của Albert cũng như Gordans khá hữu ích.

for i in $(ls -d /pathto/parent/folder/*/); do echo ${i%%/}; done

Đầu ra:

/pathto/parent/folder/childfolder1/
/pathto/parent/folder/childfolder2/
/pathto/parent/folder/childfolder3/
/pathto/parent/folder/childfolder4/
/pathto/parent/folder/childfolder5/
/pathto/parent/folder/childfolder6/
/pathto/parent/folder/childfolder7/
/pathto/parent/folder/childfolder8/


1

Đây là những gì tôi sử dụng để liệt kê chỉ tên thư mục:

ls -1d /some/folder/*/ | awk -F "/" "{print \$(NF-1)}"

1

Để trả lời câu hỏi ban đầu, */không có gì phải làm với lsmỗi người; nó được thực hiện bởi shell / Bash, trong một quá trình được gọi là continbing .

Đây là lý do tại sao echo */ls -d */đầu ra các yếu tố tương tự. ( -dCờ làm cho lsđầu ra tên thư mục chứ không phải nội dung của các thư mục.)


1

Đây là một biến thể sử dụng cây chỉ xuất ra tên thư mục trên các dòng riêng biệt, vâng, nó xấu nhưng hey, nó hoạt động.

tree -d | grep -E '^[├|└]' | cut -d ' ' -f2

hoặc với awk

tree -d | grep -E '^[├|└]' | awk '{print $2}'

Điều này có lẽ tốt hơn tuy nhiên và sẽ giữ lại /tên thư mục sau.

ls -l | grep "^d" | awk '{print $9}'

Điều này trông giống như Hàn Quốc!
Paschalis

0
file *|grep directory

O / P (Trên máy của tôi) -

[root@rhel6 ~]# file *|grep directory
mongo-example-master:    directory
nostarch:                directory
scriptzz:                directory
splunk:                  directory
testdir:                 directory

Đầu ra trên có thể được tinh chế nhiều hơn bằng cách sử dụng cắt.

file *|grep directory|cut -d':' -f1
mongo-example-master
nostarch
scriptzz
splunk
testdir

* could be replaced with any path that's permitted
 file - determine file type
 grep - searches for string named directory
 -d - to specify a field delimiter
 -f1 - denotes field 1

0

Lệnh ngắn nhất từng có (thực sự là hack) để chỉ liệt kê các thư mục. Nhập vào đường dẫn tuyệt đối hoặc đường dẫn tương đối của thư mục quan tâm và nhấn enter để vào thư mục quan tâm. Bây giờ gõ cdvà nhấn phím tab . Chỉ các thư mục được hiển thị bây giờ.

user@ubuntu:~/Desktop$cd <tab key>

Lệnh trên bây giờ sẽ chỉ hiển thị các thư mục trong Desktop . Thực sự ngắn nhất.


Giả định này tự động hoàn thành được kích hoạt và có thể không hoạt động trong mọi trường hợp. Ngoài ra, nếu bạn muốn làm một cái gì đó với điều này như chuyển đầu ra thành thứ gì đó, nó sẽ không hoạt động. Nhưng chỉ cần nhìn thấy đầu ra, chắc chắn rằng nó hoạt động nhanh chóng và dễ dàng.
lacostenycoder

Hoàn hảo! Tôi đã xóa "." Và sau đó nó hoạt động
user2060451
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.