Làm thế nào để bạn tránh quá nhiều biến môi trường PATH trong Windows?


115

Tôi muốn biết các cách tiếp cận mà bạn sử dụng để quản lý các tệp thực thi trong hệ thống của bạn là gì. Ví dụ: tôi có hầu hết mọi thứ có thể truy cập thông qua dòng lệnh, nhưng bây giờ tôi đến giới hạn của chuỗi đường dẫn, vì vậy tôi không thể thêm bất kỳ thư mục nào nữa.

Vậy bạn muôn đê xuât việc gi? Cách đây rất lâu, tôi đã thử sử dụng softLinks của các tệp thực thi trong một Dir thuộc về đường dẫn, nhưng cách tiếp cận đó không hiệu quả. Ném "chỉ thực thi" cho một Dir đã biết, có một số vấn đề mà hầu hết mọi ứng dụng đều yêu cầu một tập tin, vì vậy điều này cũng rất tệ. Ném tệp thực thi và tất cả các tệp của anh ta vào một Dir đã biết, mmm cái này sẽ hoạt động, nhưng khả năng xảy ra xung đột trong tên của các tệp là rất rất cao. Tạo một HardLink? tôi không biết. Bạn nghĩ sao?


Tại sao bạn sử dụng nhiều đường dẫn? đường dẫn thường được sử dụng cho thư mục chung, khi ứng dụng của bạn phải chia sẻ mở rộng đối tượng / ứng dụng / lib với người khác. Sử dụng rất nhiều làm cho ứng dụng bắt đầu chậm hơn. Bạn có thể cung cấp thêm chi tiết về cách bạn sử dụng, tạo môi trường đường dẫn var?
pinichi

1
xin chào pinichi, rất nhiều ứng dụng sử dụng tiêu chuẩn "C: \ Program File \ AppNAme \ ..." và trong trường hợp của tôi, rất nhiều ứng dụng này có thể chạy theo kiểu dòng lệnh hoặc cần truy cập vào các ứng dụng khác ( ví dụ, các tệp thực thi của Miktex mà bất kỳ trình soạn thảo Tex nào cũng mong đợi tồn tại), vì vậy chúng cần phải ở trong PATH. Tôi sẽ không biết một cách tiếp cận tốt hơn bởi vì tôi không bền vững
mjsr

1
Công cụ này sẽ nén các đường dẫn. Kết quả thật ấn tượng: uweraabe.de/Blog/2014/09/09/the-garbled-path-variable/#more-337
InTheNameOfScience

Câu trả lời:


84

Một cách tôi có thể nghĩ đến là sử dụng các biến môi trường khác để lưu trữ các đường dẫn một phần; ví dụ, nếu bạn có

C:\this_is_a\long_path\that_appears\in_multiple_places\subdir1;
C:\this_is_a\long_path\that_appears\in_multiple_places\subdir2;

sau đó bạn có thể tạo một biến môi trường mới, chẳng hạn như

SET P1=C:\this_is_a\long_path\that_appears\in_multiple_places

sau đó đường dẫn ban đầu của bạn trở thành

%P1%\subdir1;
%P1%\subdir2;

EDIT: Một tùy chọn khác là tạo một binthư mục chứa .batcác tệp trỏ đến các .exetệp thích hợp .

EDIT 2: Nhận xét của Ben Voigt cho một câu trả lời khác đề cập rằng việc sử dụng các biến môi trường khác như được đề xuất có thể không làm giảm thời lượng %PATH%vì chúng sẽ được mở rộng trước khi được lưu trữ. Điều này có thể đúng và tôi chưa thử nghiệm cho nó. Một tùy chọn khác là sử dụng các biểu mẫu 8dot3 cho các tên thư mục dài hơn, ví dụ như C:\Program Filesthường tương đương với C:\PROGRA~1. Bạn có thể sử dụng dir /xđể xem tên ngắn hơn.

EDIT 3: Bài kiểm tra đơn giản này khiến tôi tin rằng Ben Voigt đã đúng.

set test1=hello
set test2=%test1%hello
set test1=bye
echo %test2%

Vào cuối này, bạn thấy đầu ra hellohellochứ không phải byehello.

EDIT 4: Trong trường hợp bạn quyết định sử dụng các tệp bó để loại bỏ một số đường dẫn nhất định %PATH%, bạn có thể quan tâm đến cách chuyển các đối số từ tệp bó của mình sang tệp thực thi để quy trình được minh bạch (nghĩa là bạn sẽ không nhận thấy sự khác biệt nào giữa việc gọi tệp bó và gọi tệp thực thi). Tôi không có nhiều kinh nghiệm viết các tệp bó, nhưng điều này có vẻ hoạt động tốt.

@echo off

rem This batch file points to an executable of the same name
rem that is located in another directory. Specify the directory
rem here:

set actualdir=c:\this_is\an_example_path

rem You do not need to change anything that follows.

set actualfile=%0
set args=%1
:beginloop
if "%1" == "" goto endloop
shift
set args=%args% %1
goto beginloop
:endloop
%actualdir%\%actualfile% %args%

Theo nguyên tắc chung, bạn nên cẩn thận về việc chạy các tệp bó từ internet, vì bạn có thể thực hiện tất cả mọi thứ với các tệp bó như định dạng ổ cứng của bạn. Nếu bạn không tin tưởng mã ở trên (mà tôi đã viết), bạn có thể kiểm tra nó bằng cách thay thế dòng

%actualdir%\%actualfile% %args%

với

echo %actualdir%\%actualfile% %args%

Tốt nhất bạn nên biết chính xác những gì mỗi dòng làm trước khi bạn chạy nó.


1
biểu mẫu 8dot3 hoạt động tốt, nhưng đối với các thư mục thực sự lớn không quá lớn, ví dụ: "C: \ Program Files (x86) \ Microsoft Visual Studio 2008 SDK \ VisualStudioIntegration \ Tools \ Sandcastle \ ProductionTools \". Một điều khác tiết kiệm một chút là với tư cách là người dùng, tôi có thể tạo biến PATH dựa trên Đường dẫn hệ thống và thêm bất kỳ Dir nào khác. Tất cả các cách tiếp cận này thuộc loại "compact that string", nhưng chúng ta có thể có một thư mục nhị phân tập trung, như Unix không?
mjsr

1
Hmm, liên quan đến các thư mục rất dài, tôi sẽ nghĩ ngược lại là đúng: thư mục càng dài, bạn càng lưu nhiều ký tự bằng cách sử dụng định dạng 8dot3. Nếu khó điều hướng cmd, lưu ý rằng bạn có thể sử dụng *để lưu gõ. Vì vậy, ví dụ, từ root, gõ vào dir /x pro*. Bạn sẽ thấy thư mục mong muốn của mình ở đó cùng với tên 8dot3 của nó. Sau đó sử dụng cdđể điều hướng đến nó và lặp lại quá trình.
Mitch Schwartz

1
Về UNIX, có bạn $PATHhoạt động rất giống với %PATH%Windows, vì vậy tôi không chắc quan điểm của bạn là chính xác. Theo quy ước, tên thư mục UNIX có xu hướng ngắn hơn trong Windows và kết quả là $PATHcũng có xu hướng ngắn hơn.
Mitch Schwartz

2
Cảm ơn Mitch, Chỉnh sửa 4 mà bạn cung cấp là những gì tôi muốn!, Bây giờ tôi có thể có thư mục tập trung với tất cả các nhị phân mà tôi cần. Tôi sẽ thử nghiệm sâu hơn để xem có vấn đề gì với một số ứng dụng không
mjsr

2
Phần 'chỉnh sửa 4' của bạn quá phức tạp để chuyển các đối số cho tệp thực thi. Xem câu trả lời của Michael Burr.
Dave Andersen

83

Điều này sẽ phân tích biến môi trường% PATH% của bạn và chuyển đổi từng thư mục thành tên ngắn gọn của nó và sau đó ghép tất cả lại với nhau:

@echo off

SET MyPath=%PATH%
echo %MyPath%
echo --

setlocal EnableDelayedExpansion

SET TempPath="%MyPath:;=";"%"
SET var=
FOR %%a IN (%TempPath%) DO (
    IF exist %%~sa (
        SET "var=!var!;%%~sa"
    ) ELSE (
        echo %%a does not exist
    )
)

echo --
echo !var:~1!

Lấy đầu ra và cập nhật biến PATH trong các biến môi trường.


Cảm ơn bạn rất hữu ích !! Nó rút ngắn con đường của tôi từ năm 1990 đến 1338 và mọi thứ vẫn hoạt động như một cơ duyên! Tôi đã chọn cách tiếp cận của bạn vì tôi muốn giữ mọi thứ trong đường dẫn của mình và việc liên kết qua các tệp bó sẽ quá tốn thời gian. CẢM ƠN!
ndrizza

2
Điều này cũng hữu ích vì nó cho tôi biết về tất cả các thư mục không tồn tại trong đường dẫn của tôi đã tích lũy theo thời gian.
Nate Glenn

27
Rapid môi trường biên tập là một cách khác để làm điều này. Nó làm nổi bật các thư mục không tồn tại và có tùy chọn "chuyển đổi đường dẫn dài thành ngắn".
Russell Gallop

3
@RussellGallop: đó, thưa ngài, là một công cụ tuyệt vời.
elo80ka

1
Lưu ý báo giá đóng cửa còn thiếu sau %%~sa. Tôi đã cố cập nhật câu trả lời nhưng nó sẽ không cho phép tôi trừ khi tôi thay đổi 6 ký tự
zr870

28

nếu bạn đang sử dụng windows vista hoặc cao hơn, bạn có thể tạo một liên kết tượng trưng đến thư mục. ví dụ:

mklink /d C:\pf "C:\Program Files"

sẽ tạo một liên kết vì vậy c:\pfsẽ là program filesthư mục của bạn . Tôi đã loại bỏ 300 ký tự khỏi đường dẫn của mình bằng cách sử dụng thủ thuật này.


Đây là một thay thế tốt cho việc sử dụng biến môi trường để biểu diễn một phần đường dẫn.
porcus

1
Điều này chắc chắn sẽ giúp nhiều hơn, nhưng nếu bạn muốn một giải pháp "chính xác" hơn (được hỗ trợ?), Bạn có thể thay thế các trường hợp c:\Program Filesc:\Program Files (x86)bằng các biến được xác định trước %ProgramFiles%%ProgramFiles(x86)% tenforums.com/tutorials/. Những điều này chỉ lưu một vài ký tự , nhưng nếu bạn THỰC SỰ sắp sửa tối đa PATH, thì đó có thể là sự khác biệt. Đối với vấn đề đó, tôi sẽ tạo% pf% và% pfx% để giải quyết các đường dẫn chính xác. Cảm ơn ý kiến! :)
rainabba

Vấn đề với việc sử dụng các biến như% pf% và% pfx% là bạn có cùng các vấn đề như tạo liên kết tượng trưng - cập nhật phần mềm có thể thêm lại mọi thứ vào biến đường dẫn. Vấn đề khác với việc sử dụng các biến như thế là nó không nhanh chóng và dễ dàng để tạo ra những thứ xung quanh nó hoặc duyệt chúng từ explorer. Sử dụng phương pháp tôi đã mô tả, bạn có thể mở c: \ PF theo nghĩa đen. Windows thấy nó giống như một thư mục để bạn có thể viết bat hoặc powershell chống lại nó khá dễ dàng.
bmg002

10

Trong trường hợp có ai quan tâm ...

Tôi thấy tôi không bao giờ thực sự cần tất cả các đường dẫn đó cùng một lúc, vì vậy tôi tạo ra một loạt các tệp bó "khởi tạo" để sửa đổi đường dẫn cho phù hợp.

Ví dụ, nếu tôi muốn thực hiện một số phát triển C ++ trong Eclipse, tôi sẽ làm:

> initmingw
> initeclipse
> eclipse

Điều này cũng thuận tiện để tránh xung đột giữa các tệp thực thi có cùng tên (chẳng hạn như trình biên dịch C ++ và D, cả hai đều có make.exe).

Các tệp bó của tôi thường trông như thế này:

@echo off
set PATH=C:\Path\To\My\Stuff1;%PATH%
set PATH=C:\Path\To\My\Stuff2;%PATH%

Tôi thấy cách tiếp cận này tương đối sạch sẽ và chưa gặp phải bất kỳ vấn đề nào với nó.


7

Nói chung tôi không phải lo lắng về điều này (Tôi chưa gặp phải giới hạn kích thước đường dẫn - tôi thậm chí không biết đó là gì trên các hệ thống Windows hiện đại), nhưng đây là những gì tôi có thể làm để tránh đưa thư mục của chương trình vào con đường:

  • hầu hết các tiện ích dòng lệnh được ném vào một c:\utilthư mục trên đường dẫn
  • nếu không, tôi sẽ thêm một tệp cmd / batch đơn giản vào c:\utilthư mục trông giống như:

    @"c:\program files\whereever\foo.exe" %*
    

mà về cơ bản tạo ra một bí danh cho lệnh. Nó không nhất thiết phải hoàn hảo. Một số chương trình thực sự khăng khăng đi theo con đường (ngày nay khá hiếm) và các chương trình khác cố gắng gọi nó có thể không tìm thấy nó đúng cách. Nhưng đối với hầu hết các sử dụng, nó hoạt động tốt.

Nhưng nhìn chung, tôi không phải lo lắng về việc tránh thêm thư mục vào đường dẫn.


Khi bạn bắt đầu theo "đường dẫn" này, hãy cẩn thận rằng tập lệnh bó có thể gọi tập lệnh bó khác mà không cần cú pháp "GỌI [bat]". Vì vậy, nếu bạn muốn đảm bảo exe chuyển tiếp hoặc không được gọi từ một con dơi, hãy sử dụng "gọi php script.php" thay vì chỉ "php script.php" (hoạt động cả hai cách) Một lý do tuyệt vời để sử dụng. người điều phối bat là để ngăn chặn xung đột tên PATH (phiên bản bội của cùng một exe)
131

@ 131: Bạn có phiền giải thích ý của bạn về "con đường" này không? Bạn có nghĩa là đường dẫn tệp cụ thể từ ví dụ? Hay đúng hơn là phương pháp chung được đề xuất bởi câu trả lời này?
HOẶC Mapper

@ORMapper: khi 131 nói, 'Khi bạn bắt đầu đi theo "con đường" này, anh ta có nghĩa là,' Khi bạn sử dụng kỹ thuật này '.
Michael Burr

1
Như bạn đã viết, "các chương trình khác cố gắng gọi nó có thể không tìm thấy đúng" - trường hợp cụ thể: Thật không may, nó có thể ném các cuộc gọi tự động đến các ứng dụng dòng lệnh, ví dụ như bằng các công cụ xây dựng như NAnt, tất nhiên. Một biện pháp khắc phục đang gọi lệnh với một lệnh được chuẩn bị trước cmd /c, nhưng điều đó có nghĩa là tập lệnh xây dựng trở thành đặc thù của Windows: / Tôi đã hỏi về điều đó trong một câu hỏi riêng biệt .
HOẶC Mapper

5

Một ý tưởng khác: Sử dụng DIR / X để xác định tên ngắn được tạo cho tên tệp không phải là 8dot3. Sau đó sử dụng chúng trong% PATH% của bạn.

Ví dụ: 'C: \ Tệp chương trình' trở thành 'C: \ PROGRA ~ 1'.


1
Rất tiếc, tôi mới nhận ra điều này đã được đề xuất bởi @Mitch. Tôi sẽ cho anh ta +1 cho điều đó. :)
Android đêm


2

Tôi đã viết và sử dụng mọi lúc mọi nơi một luồng tiêu chuẩn (stdin / stderr / stdout) & mã thoát chương trình PROXY (được gọi là bộ điều phối https://github.com/131/dispatcher )

Tất cả chương trình CLI tôi sử dụng (nút, php, python, git, svn, rsync, plink ...) tôi đang sử dụng thực sự là cùng một tệp exe (khoảng 10kb, mà tôi chỉ đặt tên khác nhau), mà tôi đặt cùng một tên danh mục. Một tệp văn bản rõ ràng tĩnh giả làm "tên tệp proxy để ánh xạ exe thực".

Bộ điều phối sử dụng API win32 quản lý quy trình cấp thấp để minh bạch tuyệt đối.

Sử dụng phần mềm này, tôi chỉ có MỘT thư mục bổ sung được đặt trong PATH cho tất cả các chương trình tôi có thể sử dụng.


1

Tạo thư mục c: \ bin thêm vào đường dẫn của bạn và liên kết cứng như bạn đã nói có thể rút ngắn chuỗi. Có thể thêm một biến pf vào các vars hệ thống với giá trị c: \ Tệp chương trình, sau đó thay thế c: \ Tệp chương trình bằng% pf% trong đường dẫn.

Biên tập:

Tạo một ổ đĩa ảo. trạm biến áp p: "c: \ chương trình"


1
Tôi nghĩ rằng đường dẫn sẽ chứa các biến mở rộng, trong trường hợp đó, nó sẽ không rút ngắn được nữa.
Ben Voigt

0

Tôi làm theo các bước sau để làm cho các mục có thể quản lý được:

  1. Tạo người dùng khác nhau cho sự kết hợp khác nhau của việc sử dụng gói phần mềm. Ví dụ: (a) Tạo một web người dùng để cung cấp tất cả các phần mềm phát triển web; (b) Tạo cơ sở dữ liệu người dùng để cung cấp tất cả các gói phần mềm lưu trữ dữ liệu và cơ sở dữ liệu. Hãy nhớ rằng một số phần mềm có thể tạo nhiều hơn một mục. Hoặc đôi khi tôi chia điều này thành người dùng cụ thể và nhà tiên tri cụ thể của MSSQL. Tôi đặt MySQL / PostgreSQL, tomcat, wamp, xamp all vào tài khoản người dùng webr.

  2. Nếu có thể hãy cài đặt các gói phổ biến như office, photoshop, .. như hệ thống cụ thể có sẵn cho tất cả người dùng và các gói đặc biệt như người dùng cụ thể. Tất nhiên tôi phải đăng nhập vào những người dùng khác nhau và cài đặt chúng. Không phải tất cả các phần mềm có thể cung cấp tùy chọn này. Nếu tùy chọn "chỉ cài đặt cho người dùng này" không có sẵn, hãy cài đặt nó cho toàn hệ thống.

  3. Tôi tránh cài đặt các chương trình vào thư mục Tệp chương trình (x86) hoặc vào Tệp chương trình. Tôi luôn luôn cài đặt vào thư mục cơ sở. Ví dụ: MySQL 64 bit đi vào "C: \ mysql64" và MySQL 32 bit đi vào thư mục "C: \ mysql". Tôi luôn giả sử thêm một hậu tố 64 chỉ cho phần mềm 64 bit. Nếu không có hậu tố, thì đó là 32 bit. Tôi làm theo điều tương tự với Java và những người khác. Bằng cách này, đường dẫn của tôi sẽ ngắn hơn, không bao gồm "C: \ Chương trình tệp (x86)". Đối với một số phần mềm, tệp cấu hình có thể cần được chỉnh sửa để hiển thị chính xác tệp .exe nằm ở đâu. Chỉ chương trình yêu cầu được cài đặt vào "C: \ Program File (x86)" mới được cài đặt vào thư mục đó. Luôn luôn tôi nhớ để rút ngắn tên. Tôi tránh số phiên bản như tomcat / phát hành / phiên bản-2.5.0.3 chi tiết như vậy. Nếu tôi cần phiên bản biết, Tôi tạo một tập tin theo tên phiên bản và đặt nó vào thư mục tomcat. Nói chung rút ngắn liên kết càng nhiều càng tốt.

  4. Bao gồm bất kỳ lô nào để thay thế liên kết viết tắt cho đường dẫn, nếu tất cả các bước trên vượt qua giới hạn Windows.

Sau đó Đăng nhập vào sử dụng cụ thể (ứng dụng di động hoặc kho dữ liệu / kho dữ liệu hoặc phát triển web .. ..) và thực hiện các tác vụ có liên quan.

Bạn cũng có thể tạo các cửa sổ ảo trong các cửa sổ. Miễn là bạn có một bản sao hệ điều hành được cấp phép, việc tạo nhiều cửa sổ ảo với cùng một khóa là có thể. Bạn có thể đặt các gói cụ thể cho một tác vụ cụ thể trong máy đó. Bạn phải khởi chạy VM riêng mỗi lần. Một số gói sử dụng nhiều bộ nhớ như các nhà sản xuất phim hoạt hình 3D nên được đưa vào máy chính chứ không phải vào VM vì VM sẽ chỉ có một phần RAM có sẵn để sử dụng. Mặc dù vậy, việc khởi động từng VM là một nỗi đau.


0

Các giải pháp trên chỉ hoạt động nếu bạn có thể cắt giảm con đường của bạn. Trong trường hợp của tôi, đó thực sự không phải là một lựa chọn và thật rắc rối khi phải chạy một kịch bản mỗi khi tôi mở một dấu nhắc lệnh. Vì vậy, tôi đã viết một tập lệnh đơn giản chạy tự động khi mở dấu nhắc lệnh và nối thêm nội dung của tệp văn bản vào đường dẫn của bạn.

Ngoài ra còn có một số bối cảnh khi chạy tập lệnh này phá vỡ mọi thứ (giả sử, trong vỏ github hoặc cygwin), vì vậy tôi cũng đã thêm một tệp chứa danh sách các đường dẫn, nếu dấu nhắc lệnh được bắt đầu trong chúng, biến đường dẫn không phải là Không thay đổi thông qua tập lệnh khởi động thường cập nhật đường dẫn.

@echo off

:: Modify these to the actual paths of these two files
set dontSetupFile=C:\Users\Yams\Dontsetup.txt
set pathFile=C:\Users\Yams\Path.txt

:: Retrieve the current path (for determining whether or not we should append to our path)
set curDir=%cd%

:: Be done if the current path is listed in the dontSetupFile
SetLocal EnableDelayedExpansion
for /F "delims=" %%i in (%dontSetupFile%) do (
    if "%%i"=="%curDir%" GOTO AllDone
)



:: Append the pathFile to our current PATH
set pathAppend=
for /F "delims=" %%i in (%pathFile%) do (set pathAppend=!pathAppend!%%i)

set PATH=%PATH%;%pathAppend%


:: The only way to actually modify a command prompt's path via a batch file is by starting
::   up another command prompt window. So we will do this, however, if this script is
::   automatically called on startup of any command prompt window, it will infinately 
::   recurse and bad things will happen.

:: If we already ran, we are done
if "%yams%"=="onion" GOTO AllDone

:: Otherwise, flag that we just ran, and then start up a new command prompt window
::   with this flag set
set yams=onion

cmd \K set PATH=%PATH%;

:: When that command prompt exits, it will load back up this command prompt window, and
::   then the user will need to exit out of this as well. This causes this window to
::   automatically exit once the cmd it just spawned is closed.
exit()

:: Path is set up, we are done!
:AllDone
@echo on

Và Path.txt sẽ trông giống như

C:\Program Files (x86)\Google\google_appengine;
C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;
C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;
C:\Program Files\Microsoft SQL Server\110\Tools\Binn;
C:\Program Files\Microsoft DNX\Dnvm;
C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit;

Trong khi Dontsetup.txt sẽ trông giống như

C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit
C:\Program Files (x86)\Git\cmd
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

Để thực hiện việc này tự động khi khởi động, hãy mở regedit, điều hướng đến HKEY_LOCAL_MACHINE / SOFTWARE / Microsoft / Command Processor, sau đó nhấp chuột phải vào bên phải và nhấn mới -> Giá trị nhiều chuỗi. Đặt tên là AutoRun. Đặt giá trị của nó thành

C:\Users\Yams\setUpPath.bat

hoặc bất cứ nơi nào khác bạn lưu trữ các tập tin hàng loạt ở trên.


0

Không thử nó, nhưng sẽ tách PATH trong các phần hoạt động và tham gia chúng vào công việc biến cuối cùng?

Ví dụ ban đầu hãy nói rằng bạn có một cái gì đó như

PATH={LONGPATH1};{LONGPATH2};....{2048th char}....{LONGPATH_N-1};{LONGPATH_N}

Thay vào đó bạn tạo:

_PATH1 = {LONGPATH1};{LONGPATH2};....{2048 char}
_PATH2 = {2049th char}...{LONGPATH_N-1};{LONGPATH_N}
rem // may be more parts
PATH = %_PATH1%;%_PATH2%
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.